<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Codingly &#187; OOP</title>
	<atom:link href="http://codingly.com/tag/oop/feed/" rel="self" type="application/rss+xml" />
	<link>http://codingly.com</link>
	<description>Par Romain Verdier</description>
	<lastBuildDate>Thu, 24 May 2012 09:10:53 +0000</lastBuildDate>
	<language>fr</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='codingly.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://0.gravatar.com/blavatar/62c090a2ec42be744d871177bd874854?s=96&#038;d=http%3A%2F%2Fs2.wp.com%2Fi%2Fbuttonw-com.png</url>
		<title>Codingly &#187; OOP</title>
		<link>http://codingly.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://codingly.com/osd.xml" title="Codingly" />
	<atom:link rel='hub' href='http://codingly.com/?pushpress=hub'/>
		<item>
		<title>Le mot clé yield et les itérateurs en C#</title>
		<link>http://codingly.com/2008/04/28/le-mot-cle-yield-et-les-iterateurs-en-c/</link>
		<comments>http://codingly.com/2008/04/28/le-mot-cle-yield-et-les-iterateurs-en-c/#comments</comments>
		<pubDate>Mon, 28 Apr 2008 21:40:26 +0000</pubDate>
		<dc:creator>Romain Verdier</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[LINQ]]></category>
		<category><![CDATA[OOP]]></category>

		<guid isPermaLink="false">http://romainverdier.wordpress.com/?p=24</guid>
		<description><![CDATA[Cet article est un peu particulier. D&#8217;une part, il s&#8217;agit de mon premier vrai post, et d&#8217;autre part, j&#8217;ai choisi de traiter en détail un sujet pas forcément nouveau et surtout très spécifique : les itérateurs et le mot clé yield de C#. C&#8217;est pas ma faute, tout le monde (ou presque) s&#8217;en fout, ou [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=codingly.com&#038;blog=3510695&#038;post=24&#038;subd=romainverdier&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Cet article est un peu particulier. D&#8217;une part, il s&#8217;agit de mon premier vrai post, et d&#8217;autre part, j&#8217;ai choisi de traiter en détail un sujet pas forcément nouveau et surtout très spécifique : les itérateurs et le mot clé <code>yield</code> de C#.</p>
<p>C&#8217;est pas ma faute, tout le monde (ou presque) s&#8217;en fout, ou ne sait pas qu&#8217;il existe.</p>
<p>Pourtant, ce mécanisme n&#8217;est pas seulement troublant, il est également puissant lorsqu&#8217;on l&#8217;utilise en maitrisant son fonctionnement. S&#8217;il me fallait trouver un exemple, je parlerais de l&#8217;implémentation principale de <a href="http://msdn2.microsoft.com/en-us/vbasic/aa904594.aspx">LINQ</a>, basée sur les itérateurs et le mot clé <code>yield</code>.</p>
<div class="note">Tout ce qui va suivre n&#8217;est pas forcément utile si vous cherchez simplement à savoir comment <strong>utiliser</strong> le mot clé <code>yield</code>. Par contre, si vous cherchez à <strong>comprendre</strong> le fonctionnement réel des itérateurs, il peut s&#8217;agir d&#8217;un bon point de départ. C&#8217;est un peu le parallèle que l&#8217;on pourrait faire entre la <a href="http://msdn2.microsoft.com/fr-fr/default.aspx">MSDN</a> et le livre des <a href="http://msdn2.microsoft.com/en-us/vcsharp/aa336809.aspx">spécifications de C#</a>.</div>
<p><span id="more-24"></span></p>
<h3>Rappel : le pattern Iterator selon .NET</h3>
<p>En .NET, ce <a href="http://en.wikipedia.org/wiki/Iterator_pattern">pattern</a> est quasiment natif, puisque le framework propose deux interfaces incontournables : <code><a href="http://msdn2.microsoft.com/fr-fr/library/system.collections.ienumerable.aspx">IEnumerable</a></code> et <code><a href="http://msdn2.microsoft.com/fr-fr/library/system.collections.ienumerator.aspx">IEnumerator</a></code>. Depuis C# 2.0 on trouve également les versions génériques : <code><a href="http://msdn2.microsoft.com/fr-fr/library/system.collections.ienumerator.aspx">IEnumerable&lt;T&gt;</a></code> et <code><a href="http://msdn2.microsoft.com/fr-fr/library/78dfe2yb.aspx">IEnumerator&lt;T&gt;</a></code> :</p>
<p><pre class="brush: csharp;">
public interface IEnumerable
{
    IEnumerator GetEnumerator();
}

public interface IEnumerator
{
    bool MoveNext();
    object Current { get; }
    void Reset();
}

public interface IEnumerable&lt;T&gt; : IEnumerable
{
    IEnumerator&lt;T&gt; GetEnumerator();
}

public interface IEnumerator&lt;T&gt; : IEnumerator, IDisposable
{
    T Current { get; }
}
</pre></p>
<p>Quand je dis qu&#8217;elles sont incontournables, ce n&#8217;est pas exagéré. Toutes les collections du framework les implémentent (y compris les tableaux) et il existe même en C# un mot clé permettant d&#8217;itérer sur ces collections : <code><a href="http://msdn2.microsoft.com/en-us/library/ttw7t8t6.aspx">foreach</a></code>.</p>
<p><pre class="brush: csharp;">
// ferns est ici une collection quelconque implémentant IEnumerable.
foreach(Fern fern in ferns)
{
   Console.WriteLine(fern);
}
</pre></p>
<p>On pense souvent que <code>foreach</code> ne peut être utilisé que sur des <code>IEnumerable</code> mais ce n&#8217;est pas tout à fait exact. Le mot clé fonctionne sur n&#8217;importe quel type possédant une méthode publique <code>GetEnumerator</code> retournant un <code>IEnumerator</code>, même si ce type n&#8217;est pas une implémentation d&#8217;<code>IEnumerable</code>.</p>
<p>Et que fait réellement le <code>foreach</code> ? Il suffit de demander à <a href="http://www.aisto.com/roeder/dotnet/">Reflector</a>. En gros, l&#8217;itération utilisant le <code>foreach</code> précédent produit le code IL correspondant à :</p>
<p><pre class="brush: csharp;">
IEnumerator enumerator = ferns.GetEnumerator();
try
{
   while (enumerator.MoveNext())
   {
       object current = enumerator.Current;
       Console.WriteLine(current);
   }
}
finally
{
   IDisposable e = enumerator as IDisposable;
   if (e != null)
   {
       e.Dispose();
   }
}
</pre></p>
<p>On note qu&#8217;il n&#8217;y a rien de magique, et que le code produit est identique à celui qu&#8217;on aurait pu écrire à l&#8217;aide d&#8217;un <code>while</code>. Il est intéressant de constater qu&#8217;il n&#8217;y a effectivement aucun lien entre le <code>foreach</code> et l&#8217;interface <code>IEnumerable</code>. Le compilateur C# se contente d&#8217;inspecter le type sur lequel itérer pour récupérer les métadonnées relatives à une méthode <code>GetEnumerator</code>. S&#8217;il ne les trouve pas, il lève une erreur durant la génération, et s&#8217;il les trouve, il produit simplement le bytecode d&#8217;appel à cette méthode. <code>IEnumerable</code> est donc dans le cas précis du <code>foreach</code> une abstraction impunément méprisée par le compilateur.</p>
<p>Lorsqu&#8217;on cherche à implémenter le pattern iterator en .NET, la solution la plus sage est donc de s&#8217;appuyer sur les interfaces du framework. En gros, pour rendre un type énumérable, on doit faire deux choses :</p>
<ul>
<li>Implémenter <code>IEnumerable</code> ou <code>IEnumerable&lt;T&gt;</code> au niveau du type à itérer.</li>
<li>Implémenter un itérateur qui respecte l&#8217;interface <code>IEnumerator</code> ou <code>IEnumerator&lt;T&gt;</code>, respectivement.</li>
</ul>
<p>Généralement, on ne se choisit pas d&#8217;implémenter uniquement ces interfaces, mais plutôt les interfaces des collections de base du framework, qui implémentent elles-mêmes <code>IEnumerable</code> et/ou <code>IEnumerable&lt;T&gt;</code> : <code>ICollection</code>, <code>IList</code>, <code>IDictionary</code>, etc. Je ne vais pas surcharger le post en publiant un exemple, mais si vous en voulez un significatif vous pouvez utiliser Reflector pour désassembler la classe générique <code>List&lt;T&gt;</code> du framework et son <code>Enumerator&lt;T&gt;</code> imbriqué. Vous verrez que le code nécessaire à la gestion des itérations est simple, mais nécessite tout de même une soixantaine de lignes.</p>
<h3>Dissection de yield</h3>
<p>Dans le titre de l&#8217;article, &#8220;yield&#8221; est placé avant &#8220;itérateurs&#8221;. C&#8217;est une forme de marketing. En réalité, il est plutôt question ici des itérateurs (iterators), qui constituent une fonctionnalité de C# 2.0. Il est important de préciser à ce stade que lorsqu&#8217;on parle d&#8217;itérateurs, on ne parle pas des types implémentant <code>IEnumerator</code>, mais bien d&#8217;un mécanisme propre au langage C# basé sur l&#8217;utilisation d&#8217;un nouveau mot clé : <code>yield</code>.</p>
<h4>Qu&#8217;est-ce qu&#8217;un itérateur ?</h4>
<p>Un itérateur est une méthode, un opérateur ou un getter dont :</p>
<ul>
<li>Le type de retour est un des suivants : <code>IEnumerable</code>, <code>IEnumerable&lt;T&gt;</code>, <code>IEnumerator</code>, <code>IEnumerator&lt;T&gt;</code>.</li>
<li>Le corps contient le mot clé <code>yield</code>.</li>
</ul>
<p>Par convention, on considère que l&#8217;implémentation d&#8217;une telle méthode (ou opérateur, ou getter) est un <strong>bloc itérateur</strong>. Un bloc itérateur a pour but de produire une séquence de valeurs du même type. Le type des valeurs retournées est appelé <strong><em>yield type</em></strong>. Lorsque le bloc itérateur retourne un <code>IEnumerable</code> ou un <code>IEnumerator</code>, le <em>yield type</em> est <code>object</code>. Lorsque le bloc itérateur retourne un <code>IEnumerable&lt;T&gt;</code> ou un <code>IEnumerator&lt;T&gt;</code>, le <em>yield type</em> est <code>T</code>.</p>
<h4>Usage de yield</h4>
<p>Un bloc itérateur est caractérisé par l&#8217;utilisation du mot clé <code>yield</code>. Ce dernier peut apparaître sous deux formes différentes. Je les cite maintenant, mais nous détaillerons plus tard :</p>
<ul>
<li><code>yield return &lt;expression&gt;;</code></li>
<li><code>yield break;</code></li>
</ul>
<p>Il faut juste garder en tête que dans la première forme, le <em>yield type</em> du bloc itérateur doit être assignable à partir du type de <code>&lt;expression&gt;</code>.</p>
<p>En introduction, et pour illustrer son utilisation, prenons un exemple simple : une méthode capable de retourner les 10 premiers entiers strictement positifs. L&#8217;implémentation la plus naïve qui me vient à l&#8217;esprit est la suivante :</p>
<p><pre class="brush: csharp;">
public IEnumerable&lt;int&gt; GetFirstTenIntegers()
{
    List&lt;int&gt; result = new List&lt;int&gt;();
    for (int i = 1; i &lt;= 10; i++)
    {
        result.Add(i);
    }
    return result;
}
</pre></p>
<p>Avec <code>yield</code>, il devient possible d&#8217;écrire :</p>
<p><pre class="brush: csharp;">
public IEnumerable&lt;int&gt; GetFirstTenIntegers()
{
    for (int i = 1; i &lt;= 10; i++)
    {
        yield return i;
    }
}
</pre></p>
<h4>Principe</h4>
<p>Essayons à présent de comprendre comment fonctionnent les itérateurs en C# et que signifie le mot clé <code>yield</code>. Avant C# 2.0, cette notion n&#8217;existait pas donc le mot clé n&#8217;existait pas. Il s&#8217;agit maintenant d&#8217;un mot clé <strong>contextuel</strong>, pour des raisons de compatibilité descendante. Un mot clé contextuel est un mot clé qui revêt sa signification spéciale uniquement dans un contexte particulier. Ici, <code>yield</code> est interprété comme un mot clé lorsqu&#8217;il est utilisé avant <code>return</code> ou <code>break</code>, mais sinon il est tout à fait possible de l&#8217;utiliser comme identifiant pour une variable, un membre, un type, etc.</p>
<p>La chose la plus importante à savoir quant au fonctionnement des itérateurs est qu&#8217;un bloc itérateur n&#8217;est pas un bloc de code qui sera exécuté, mais un bloc de code qui sera <strong>interprété par le compilateur pour générer du code</strong>. Il ne s&#8217;agit pas d&#8217;une simple nuance, et je vais essayer de détailler.</p>
<p>Durant la génération, voici ce qui se produit lorsque le compilateur C# rencontre un bloc itérateur :</p>
<ol>
<li>Si le type de retour du bloc est <code>IEnumerator</code> ou <code>IEnumerator&lt;T&gt;</code>, le compilateur fera en sorte qu&#8217;un <strong>objet énumérateur</strong> soit instancié et retourné. Cet objet implémentera respectivement <code>IEnumerator</code> ou <code>IEnumerator</code> et <code>IEnumerator&lt;T&gt;</code>.</li>
<li>Si le type de retour du bloc est <code>IEnumerable</code> ou <code>IEnumerable&lt;T&gt;</code>, le compilateur fera en sorte qu&#8217;un <strong>objet énumérable</strong> soit instancié et retourné. Cet objet implémentera respectivement <code>IEnumerable</code> et <code>IEnumerator</code>, ou bien <code>IEnumerable&lt;T&gt;</code> et <code>IEnumerator&lt;T&gt;</code>, ce qui fait en fait également un objet énumérateur.</li>
</ol>
<p>Dans tous les cas, le compilateur doit générer un type pour ces objets durant la compilation. Il est facile de deviner que tout le secret du mécanisme des itérateurs réside donc dans le processus de génération de ces types.</p>
<h4>Enumerable &amp; Enumerator object</h4>
<p><strike>Répétons</strike> Résumons. Lorsque qu&#8217;un bloc itérateur est interprété, le compilateur doit générer un type d&#8217;objet énumérable ou un type d&#8217;objet énumérateur. Dans les deux cas, il doit générer l&#8217;implémentation d&#8217;<code>IEnumerator</code> et <code>IEnumerator&lt;T&gt;</code>, puisque un type d&#8217;objet énumérable est <strong>aussi</strong> un type d&#8217;objet énumérateur (cf. paragraphe précédent). Notons également que le compilateur fournit une implémentation d&#8217;<code>IDisposable</code> pour tous les objets énumérateurs.</p>
<p>Un objet énumérable doit en plus implémenter <code>IEnumerable</code> : il lui suffit de retourner un self pointer ou un clone au niveau de sa méthode <code>GetEnumerator</code>. Un objet énumérable de type <code>T</code> retournera donc une instance de type <code>T</code>.</p>
<p>Cela signifie que le type généré à la compilation pour un objet énumérateur possède les membres suivants :</p>
<ul>
<li>Méthode <code>MoveNext()</code></li>
<li>Propriété <code>Current</code></li>
<li>Méthode <code>Reset()</code></li>
<li>Méthode <code>Dispose()</code></li>
</ul>
<p>La méthode <code>MoveNext</code> est probablement la plus intéressante ici : le compilateur va faire en sorte de générer son corps de façon à ce qu&#8217;elle respecte la même logique d&#8217;itération que celle qui était décrite par le code du bloc itérateur original. Tout le challenge consiste à faire en sorte que les appels successifs à <code>MoveNext</code> contrôlent bien l&#8217;itération comme le code du bloc itérateur le spécifie. Cela implique que toutes les variables et objets sollicités au niveau du bloc itérateur par l&#8217;algorithme devront être transformés en membres d&#8217;instance de l&#8217;objet énumérateur pour que leur état soit conservé entre chaque appel.</p>
<p>Par exemple, si le bloc itérateur est le suivant :</p>
<p><pre class="brush: csharp;">
for (int i = 1; i &lt;= 10; i++)
{
    yield return i;
}
</pre></p>
<p>Le compilateur devra créer un type d&#8217;objet énumérateur possédant un champ d&#8217;instance (ex: <code>private int cpt;</code>) pour conserver l&#8217;état du compteur utilisé dans la boucle for du bloc itérateur. Lors de la création de l&#8217;objet énumérateur, son état est initialisé en fonction des valeurs initiales de toutes les variables sollicitées dans l&#8217;algorithme du bloc itérateur. Dans le cas précédent, le membre <code>cpt</code> sera initialisé avec la valeur 1.</p>
<p>Nous allons voir à présent comment faire la correspondance entre le code du bloc itérateur (celui que vous écrivez) et le code de la méthode <code>MoveNext</code> (généré par le compilateur). Nous allons pour cela considérer <strong>même si c&#8217;est inexact</strong> que le flux d&#8217;exécution se déplace dans le code du bloc itérateur à chaque fois que la méthode <code>MoveNext</code> est appelée sur l&#8217;objet énumérateur.</p>
<p>Lors du premier appel à la méthode <code>MoveNext</code>, le bloc itérateur est exécuté à partir de la première ligne jusqu&#8217;à ce que l&#8217;exécution soit interrompue par l&#8217;une des conditions suivantes :</p>
<ul>
<li><strong>L&#8217;expression <code>yield return &lt;expression&gt;;</code> est rencontrée</strong> : La valeur de <code>&lt;expression&gt;</code> est affectée à la propriété <code>Current</code> de l&#8217;objet énumérateur, et toutes les valeurs des variables locales utilisées par le bloc itérateur sont sauvegardées. La méthode <code>MoveNext</code> retourne <code>true</code>.</li>
<li><strong>L&#8217;expression <code>yield break;</code> est rencontrée</strong> : Si une clause <code>finally</code> existait pour le code contenant le <code>yield break</code>, elle est exécutée. La méthode <code>MoveNext</code> retourne <code>false</code>.</li>
<li><strong>La fin du bloc itérateur est rencontré</strong> :  La méthode <code>MoveNext</code> retourne <code>false</code>.</li>
<li><strong>Une exception non capturée est levée</strong> : Si une clause <code>finally</code> existe pour le code ayant levé l&#8217;exception, elle est exécutée. L&#8217;exception est propagée à l&#8217;appelant de la méthode <code>MoveNext</code>.</li>
</ul>
<p>Lors des appels subséquents à la méthode <code>MoveNext</code>, la même logique est respectée à la différence près que l&#8217;exécution du bloc itérateur reprend <strong>à la ligne suivant l&#8217;expression yield return ayant provoqué la sortie précédente de la méthode</strong>. Il est important de savoir que l&#8217;état des variables locales ayant été sauvegardées lors de la sortie est restauré avant que l&#8217;exécution ne reprenne. Si la méthode <code>MoveNext</code> est appelée de nouveau alors qu&#8217;elle a déjà signifié la fin de l&#8217;itération en retournant <code>false</code>, elle continue à retourner <code>false</code>.</p>
<h3>Quelques exemples</h3>
<p><pre class="brush: csharp;">
public IEnumerable GetValues(int limit)
{
    for (int i = 0; i &lt; limit; i++)
    {
        if (i%2 == 0)
        {
            yield return i;
        }
    }
}

[Test]
public void Test()
{
    foreach (object o in GetValues(10))
    {
        Console.WriteLine(o);
    }
}
</pre></p>
<p>Affiche les 5 premiers nombres pairs : 0, 2, 4, 6, 8.</p>
<p><pre class="brush: csharp;">
public IEnumerable GetValues()
{
    yield return &quot;Yield&quot;;
    yield return &quot; c'est&quot;;
    yield return &quot; rigolo.&quot;;
}

[Test]
public void Test()
{
    foreach (object o in GetValues())
    {
        Console.Write(o);
    }
}
</pre></p>
<p>Affiche : &#8220;Yield c&#8217;est rigolo&#8221;. Plusieurs <code>yield return</code> peuvent se succéder dans le même bloc itérateur.</p>
<p><pre class="brush: csharp;">
public IEnumerable GetFStrings(IEnumerable&lt;string&gt; strings)
{
    foreach (string s in strings)
    {
        if (s.ToUpper().StartsWith(&quot;F&quot;))
        {
            yield return s;
        }
        else
        {
            yield break;
        }
    }
}

[Test]
public void Test()
{
    string[] strings = new string[]{&quot;fern&quot;,&quot;fern2&quot;,&quot;fern3&quot;,&quot;notFern&quot;, &quot;fern4&quot;};
    foreach (object o in GetFStrings(strings))
    {
        Console.WriteLine(o);
    }
}
</pre></p>
<p>Affiche les chaines de la collection qui commencent par la lettre &#8220;F&#8221;. Si une chaine de caractères ne commence pas par la lettre &#8220;F&#8221;, l&#8217;inspection est stoppée. La méthode affiche donc: &#8220;fern&#8221;, &#8220;fern2&#8243;, &#8220;fern3&#8243;.</p>
<p><pre class="brush: csharp;">
public IEnumerable GetComputedValues(int[] toCompute)
{
    foreach (int i in toCompute)
    {
        Thread.Sleep(1000);
        yield return i*i;
    }
}

[Test]
public void Test()
{
    int[] toCompute = new int[]{12,21,40,3,78};
    foreach (int computedValue in GetComputedValues(toCompute))
    {
        if (computedValue &gt; 150)
        {
            Console.WriteLine(computedValue);
            break;
        }
    }
}
</pre></p>
<p>Affiche la première valeur de l&#8217;énumération supérieure à 150, et stoppe l&#8217;itération : 441. (21²)</p>
<h3>Exécution différée et conséquences</h3>
<p>Une des caractéristiques de ce mécanisme est aussi trompeuse qu&#8217;utile. Nous avons largement insisté sur le fait que le code du bloc itérateur n&#8217;était pas réellement exécuté, et qu&#8217;il permettait simplement au compilateur de générer un objet énumérateur. Cela signifie qu&#8217;à l&#8217;appel de la méthode possédant le bloc itérateur, aucun code d&#8217;itération n&#8217;est exécuté.</p>
<p>Ce dernier est atteint lors du premier appel à la méthode <code>MoveNext</code> de l&#8217;objet énumérateur retourné par le compilateur, et continuera d&#8217;être exécuté au <strong>fur et à mesure</strong> de l&#8217;itération. Pour simplifier, et qualifier cette caractéristique, il est possible de parler d&#8217;<strong>exécution différée du bloc itérateur</strong>. Tentons de l&#8217;exploiter dans quelques exemples choisis :</p>
<p>La méthode <code>GetComputedValues</code> du dernier exemple peut être réécrite sans que le mot clé <code>yield</code> ne soit utilisé :</p>
<p><pre class="brush: csharp;">
public IEnumerable GetComputedValues(int[] toCompute)
{
    List&lt;int&gt; computedValues = new List&lt;int&gt;();
    foreach (int i in toCompute)
    {
        Thread.Sleep(1000);
        computedValues.Add(i*i);
    }
    return computedValues;
}
</pre></p>
<p>La pause d&#8217;une seconde est uniquement là pour simuler une opération couteuse en temps. Reprenons ensuite le même scénario d&#8217;itération, mais avec cette version de la méthode <code>GetComputedValues</code> :</p>
<p><pre class="brush: csharp;">
[Test]
public void Test()
{
    int[] toCompute = new int[]{12,21,40,3,78,45,789,12,654,10};
    foreach (int computedValue in GetComputedValues(toCompute))
    {
        if (computedValue &gt; 150)
        {
            Console.WriteLine(computedValue);
            break;
        }
    }
}
</pre></p>
<p>On constate que même si l&#8217;itération est stoppée dans le code client dès qu&#8217;une des valeurs dépasse 150, le calcul couteux a été exécuté pour chacune des 10 valeurs de la liste d&#8217;arguments, portant le temps d&#8217;exécution de tout le scénario à environ 10 secondes. En utilisant la méthode <code>GetComputedValues</code> basée sur <code>yield</code>, on réduit le temps d&#8217;exécution du scénario à 2 secondes, puisqu&#8217;uniquement 2 calculs seront effectués.</p>
<p>Voici quelques autres exemples amusants :</p>
<p>La méthode suivante peut être appelée sans provoquer de boucle infinie, puisqu&#8217;elle est pseudo-exécutée au fur et à mesure. Il faut juste faire attention lors de l&#8217;itération, et prévoir un cas terminal au niveau du code client :</p>
<p><pre class="brush: csharp;">
public IEnumerable Get42()
{
    while (true)
    {
        yield return 42;
    }
}
</pre></p>
<p>La code suivant ne provoque pas d&#8217;exception, puisque l&#8217;itération n&#8217;a pas lieu. L&#8217;exception serait déclenchée lors de la première itération, c&#8217;est à dire lors du premier appel à la méthode <code>MoveNext</code> de l&#8217;objet énumérateur :</p>
<p><pre class="brush: csharp;">
public IEnumerable&lt;char&gt; GetChars(string s)
{
    if (string.IsNullOrEmpty(s))
    {
        throw new ArgumentNullException(&quot;s&quot;);
    }

    foreach (char c in s)
    {
        yield return c;
    }
}

[Test]
public void Test()
{
    IEnumerable&lt;char&gt; chars = GetChars(null);
}
</pre></p>
<p>L&#8217;appel à la méthode <code>TestValues</code> de la classe suivante provoque l&#8217;affichage : 0,1,2,3,4 et non pas 0,1,2,3,4,5,6,7,8,9. En effet, la logique d&#8217;énumération du bloc itérateur dépend de la valeur d&#8217;un membre d&#8217;instance de la classe <code>FunnyYieldTest</code>. A chaque appel de la méthode <code>MoveNext</code> sur l&#8217;objet énumérateur, le champ <code>counter</code> sera réévalué.</p>
<p><pre class="brush: csharp;">
public class FunnyYieldTest
{
    private int counter;

    private IEnumerable&lt;int&gt; GetValues()
    {
        for (int i = 0; i &lt; this.counter; i++)
        {
            yield return i;
        }
    }

    private void TestValues()
    {
        this.counter = 10;
        IEnumerable&lt;int&gt; values = GetValues();
        this.counter = 5;
        foreach (int i in values)
        {
            Console.WriteLine(i);
        }
    }
}
</pre></p>
<p>Les plus vicieux d&#8217;entre vous pourront même tenter de modifier la valeur du champ privé <code>counter</code> <strong>durant</strong> l&#8217;itération.</p>
<p>Bref, si vous avez l&#8217;habitude d&#8217;utiliser LINQ, cela doit vous parler. Une requête LINQ possède le même mode d&#8217;exécution différé, car elle s&#8217;appuie sur le mécanisme des blocs itérateurs de C# 2.0. Vous pouvez vous amuser à décompiler la classe <code>Enumeration</code> du namespace <code>System.Linq</code>, vous verrez que les méthodes d&#8217;extensions utilisent toutes <code>yield</code>. Enfin, le <em>retro-engineering</em> ne fera pas apparaitre le mot clé <code>yield</code> directement, mais plutôt les classes que le compilateur a généré en interprétant les blocs itérateurs.</p>
<h3>Pour aller plus loin</h3>
<p>Un bon moyen de comprendre ce qui se passe réellement lors de l&#8217;interprétation des blocs itérateurs par le compilateur consiste effectivement à inspecter le bytecode généré. Reflector permet même d&#8217;afficher le code C# correspondant au code IL, ce qui est assez pratique.</p>
<p>Reprenons la méthode <code>GetFirstTenIntegers</code> :</p>
<p><pre class="brush: csharp;">
public class YieldAutopsy
{
    public IEnumerable&lt;int&gt; GetFirstTenIntegers()
    {
        for (int i = 1; i &lt;= 10; i++)
        {
            yield return i;
        }
    }
}
</pre><br />
Le compilateur va produire le code IL correspondant à :</p>
<p><pre class="brush: csharp;">
public IEnumerable&lt;int&gt; GetFirstTenIntegers()
{
    &lt;GetFirstTenIntegers&gt;d__4 d__ = new &lt;GetFirstTenIntegers&gt;d__4(-2);
    d__.&lt;&gt;4__this = this;
    return d__;
}
</pre></p>
<p>Il s&#8217;agit de la déclaration, de l&#8217;instanciation, de l&#8217;initialisation et du retour de l&#8217;objet énumérable. Le type de l&#8217;objet énumérable généré par le compilateur possède un nom barbare : <code>&lt;GetFirstTenIntegers&gt;d__4</code>. La présence des caractères interdits dans l&#8217;identifiant permettent d&#8217;éviter tout conflit avec un type éventuellement existant dans votre projet.</p>
<p>Je ne vais pas vous copier ici tout le code de cette classe générée, mais en voici tout de même la déclaration :</p>
<p><pre class="brush: csharp;">
[CompilerGenerated]
private sealed class &lt;GetFirstTenIntegers&gt;d__0 : IEnumerable&lt;int&gt;,
                                                 IEnumerable,
                                                 IEnumerator&lt;int&gt;,
                                                 IEnumerator,
                                                 IDisposable
{
   // Membres.
}
</pre></p>
<p>On constate qu&#8217;elle implémente <code>IEnumerable</code> et <code>IEnumerator</code>, ainsi qu&#8217;<code>IDisposable</code>. Si la méthode <code>GetFirstTenIntegers</code> retournait un <code>IEnumerator</code>, la classe générée le compilateur aurait simplement implémenté <code>IEnumerator</code> et <code>IDisposable</code>. Il s&#8217;agit donc là d&#8217;un objet énumérable.</p>
<p>Lorsque les deux interfaces sus citées sont implémentées par la même classe, <code>IEnumerator</code> est souvent implémenté <strong><a href="http://msdn2.microsoft.com/en-us/library/aa288461(VS.71).aspx">explicitement</a></strong>. C&#8217;est le cas ici, et cela permet d&#8217;implémenter <code>IEnumerable</code> de la façon suivante tout en cachant les membres d&#8217;<code>IEnumerator</code> aux portions du code client manipulant la classe en tant qu&#8217;<code>IEnumerable</code> :</p>
<p><pre class="brush: csharp;">
public IEnumerator GetEnumerator()
{
    return (IEnumerator)this;
}
</pre></p>
<p>Il va falloir me faire confiance : la classe générée par le compilateur respecte le même pattern, à la différence près qu&#8217;elle doit également gérer le fait d&#8217;implémenter les versions génériques de <code>IEnumerable</code> et <code>IEnumerator</code>.</p>
<p>Ce qui nous intéresse maintenant est de voir comment le bloc itérateur a été interprété par le compilateur, et quel est le résultat concret de cette interprétation au niveau de la méthode <code>MoveNext</code> :</p>
<p><pre class="brush: csharp;">
private bool MoveNext()
{
    switch (this.&lt;&gt;1__state)
    {
        case 0:
            this.&lt;&gt;1__state = -1;
            this.&lt;i&gt;5__1 = 1;
            while (this.&lt;i&gt;5__1 &lt;= 10)
            {
                this.&lt;&gt;2__current = this.&lt;i&gt;5__1;
                this.&lt;&gt;1__state = 1;
                return true;
            Label_0046:
                this.&lt;&gt;1__state = -1;
                this.&lt;i&gt;5__1++;
            }
            break;

        case 1:
            goto Label_0046;
    }
    return false;
}
</pre></p>
<p>Ce que vous devez savoir pour comprendre ce code :</p>
<ul>
<li><code>&lt;&gt;1__state</code> est un membre privé de type entier, égal à 0 lors du premier appel de la méthode.</li>
<li><code>&lt;i&gt;5__1</code> est un membre privé de type entier.</li>
<li><code>&lt;&gt;2__current</code> est un membre privé de type entier, qui est accédé via les propriétés <code>IEnumerator.Current</code> et <code>IEnumerator&lt;int&gt;.Current</code> de la classe.</li>
<li>Les <code>goto</code>, c&#8217;est le mal. Le compilateur s&#8217;en sert parce qu&#8217;il est moins faillible que nous, et c&#8217;est très bien comme ça. D&#8217;ailleurs, même s&#8217;il est possible d&#8217;utiliser <code>goto</code> en C#, certaines limitations nous empêchent de recourir à des algorithmes tels que celui qui est généré ici. Bien que le code IL de ce dernier soit valide, le compilateur C# ne sera pas capable de détecter le label <code>Label_0046</code> si nous essayons d&#8217;écrire directement en C# le code précédent. Il lèvera l&#8217;erreur de génération CS0159 : &#8220;<em>Il n&#8217;existe pas d&#8217;étiquette &#8216;Label_0046&#8242; dans la portée de l&#8217;instruction goto</em>&#8220;.</li>
</ul>
<p>On constate donc que le code présent dans la méthode <code>MoveNext</code> a pour but de contrôler l&#8217;itération exactement comme cela a été spécifié au niveau du bloc itérateur, en utilisant le mot clé <code>yield</code>. Cette transformation est relativement complexe, puisque le compilateur est capable de convertir l&#8217;algorithme original pour produire une sorte de <a href="http://fr.wikipedia.org/wiki/Automate_fini">machine à états</a> au niveau de la classe générée, pouvant supporter l&#8217;exécution différée grâce à la persistance des valeurs des différentes variables nécessaires au contrôle de l&#8217;itération entre chaque appel à <code>MoveNext</code>.</p>
<p>Ici, il s&#8217;agissait de traduire une simple boucle <code>for</code> en conservant l&#8217;état du compteur, mais certains blocs itérateurs peuvent être éminemment plus compliqués. Prenons juste la méthode <code>GetFStrings</code> de la section d&#8217;exemple, et inspectons le code que le compilateur produit pour la méthode <code>MoveNext</code> de l&#8217;itérateur :</p>
<p><pre class="brush: csharp;">
private bool MoveNext()
{
    try
    {
        switch (this.&lt;&gt;1__state)
        {
            case 0:
                this.&lt;&gt;1__state = -1;
                this.&lt;&gt;7__wrap2 = this.strings.GetEnumerator();
                this.&lt;&gt;1__state = 1;
                while (this.&lt;&gt;7__wrap2.MoveNext())
                {
                    this.&lt;s&gt;5__1 = this.&lt;&gt;7__wrap2.Current;
                    if (!this.&lt;s&gt;5__1.ToUpper().StartsWith(&quot;F&quot;))
                    {
                        goto Label_0097;
                    }
                    this.&lt;&gt;2__current = this.&lt;s&gt;5__1;
                    this.&lt;&gt;1__state = 2;
                    return true;
                Label_008D:
                    this.&lt;&gt;1__state = 1;
                    goto Label_00A1;
                Label_0097:
                    ((IDisposable) this).Dispose();
                    break;
                Label_00A1:;
                }
                this.&lt;&gt;1__state = -1;
                if (this.&lt;&gt;7__wrap2 != null)
                {
                    this.&lt;&gt;7__wrap2.Dispose();
                }
                break;

            case 2:
                goto Label_008D;
        }

        return false;
    }
    fault
    {
        ((IDisposable) this).Dispose();
    }
}
</pre><br />
On constate que l&#8217;itérateur généré possède une référence vers la collection utilisée dans le bloc itérateur (<code>this.strings</code>) et que l&#8217;implémentation de la méthode <code>MoveNext</code> doit faire appel à l&#8217;énumérateur de cette même collection. Rien n&#8217;empêche cette dernière d&#8217;utiliser un bloc itérateur comme implémentation de sa méthode <code>GetEnumerator</code>.</p>
<p>C&#8217;est d&#8217;ailleurs le cas avec LINQ, puisque son API repose sur une <a href="http://en.wikipedia.org/wiki/Fluent_interface">Fluent Interface</a>. La requête suivante :<br />
<pre class="brush: csharp;">
IEnumerable&lt;string&gt; query = from s in names
                           where s.Length == 5
                           orderby s
                           select s.ToUpper();
</pre><br />
Peut être écrite également :<br />
<pre class="brush: csharp;">
IEnumerable&lt;string&gt; query = names
                            .Where(s =&gt; s.Length == 5)
                            .OrderBy(s =&gt; s)
                            .Select(s =&gt; s.ToUpper());
</pre></p>
<p>Où les méthodes <code>Where</code>, <code>OrderBy</code> et <code>Select</code> retournent des objets énumérables. La logique complète et <strong>réelle</strong> d&#8217;exécution d&#8217;une telle requête peut vite atteindre une complexité vertigineuse, car les objets énumérables vont se solliciter entre eux. En parallèle, l&#8217;écriture des requêtes est quasi-enfantine, tandis que l&#8217;implémentation des différents blocs itérateurs reste aisée. C&#8217;est assez révélateur de la puissance des itérateurs de C#.</p>
<h3>Quelques restrictions</h3>
<ul>
<li><code>yield</code> ne peut pas être utilisé dans un contexte unsafe.</li>
<li><code>yield</code> ne peut pas être utilisé dans une méthode anonyme ou une expression lambda.</li>
<li>Les blocs itérateurs ne peuvent être utilisés dans les méthodes acceptant des paramètres <code>out</code> et/ou <code>ref</code>.</li>
<li>La méthode <code>Reset</code> des objets énumérateurs n&#8217;est jamais implémentée par le compilateur C# :</li>
</ul>
<p><pre class="brush: csharp;">
[DebuggerHidden]
void IEnumerator.Reset()
{
    throw new NotSupportedException();
}
</pre><br />
Il est donc particulièrement dangereux d&#8217;écrire :<br />
<pre class="brush: csharp;">
IEnumerable&lt;int&gt; firstIntegers = GetFirstTenIntegers();
IEnumerator&lt;int&gt; enumerator = firstIntegers.GetEnumerator();
enumerator.Reset();
</pre></p>
<h3>Conclusion</h3>
<p>Les blocs itérateurs en C#, c&#8217;est de la magie sans être de la magie. On entend quelque fois certains détracteurs de Microsoft expliquer que C#, notamment dans les nouvelles versions, se permet de bousculer les paradigmes empiriques de l&#8217;OOP avec les itérateurs, les expressions lambda, les méthodes d&#8217;extension, LINQ, etc. Je pense que lorsqu&#8217;on prend la peine de comprendre comment ces solutions sont implémentées, on se rend compte qu&#8217;elles n&#8217;ont pour but que de rendre la vie des gens qui font de l&#8217;ordinateur plus simple. En général, il s&#8217;agit d&#8217;abstractions ou de surcouches dont les mécanismes sont basés sur les concepts fondamentaux de l&#8217;Objet.</p>
<p>J&#8217;aurais probablement l&#8217;occasion d&#8217;en reparler, mais les itérateurs en C#2.0 ou des méthodes d&#8217;extensions en C# 3.0 ne sont que des timesavers légitimes. Dans le premier cas, on délègue au compilateur une tâche pénible qu&#8217;il est parfaitement possible de définir en utilisant un enrichissement mineur du langage. Dans le second, on offre juste une façon supplémentaire d&#8217;appeler certaines méthodes statiques encapsulant un traitement relatif à un type d&#8217;objet. Etc.</p>
<p><a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fcodingly.com%2f2008%2f04%2f28%2fle-mot-cle-yield-et-les-iterateurs-en-c%2f"><img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fcodingly.com%2f2008%2f04%2f28%2fle-mot-cle-yield-et-les-iterateurs-en-c%2f&amp;border=003300&amp;bgcolor=000000&amp;cbgcolor=FFFFFF" border="0" alt="kick it on DotNetKicks.com" /></a></p>
<br /><img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/romainverdier.wordpress.com/24/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/romainverdier.wordpress.com/24/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/romainverdier.wordpress.com/24/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/romainverdier.wordpress.com/24/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/romainverdier.wordpress.com/24/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/romainverdier.wordpress.com/24/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/romainverdier.wordpress.com/24/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/romainverdier.wordpress.com/24/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/romainverdier.wordpress.com/24/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/romainverdier.wordpress.com/24/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/romainverdier.wordpress.com/24/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/romainverdier.wordpress.com/24/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/romainverdier.wordpress.com/24/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/romainverdier.wordpress.com/24/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/romainverdier.wordpress.com/24/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/romainverdier.wordpress.com/24/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=codingly.com&#038;blog=3510695&#038;post=24&#038;subd=romainverdier&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://codingly.com/2008/04/28/le-mot-cle-yield-et-les-iterateurs-en-c/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/cb0ee4bde49708f4be24a02a5d59e52e?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">Romain</media:title>
		</media:content>

		<media:content url="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fcodingly.com%2f2008%2f04%2f28%2fle-mot-cle-yield-et-les-iterateurs-en-c%2f&#38;border=003300&#38;bgcolor=000000&#38;cbgcolor=FFFFFF" medium="image">
			<media:title type="html">kick it on DotNetKicks.com</media:title>
		</media:content>
	</item>
	</channel>
</rss>
