IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Framework .NET Discussion :

usage ou non usage du mot cle YIELD dans une boucle foreach


Sujet :

Framework .NET

  1. #1
    Membre habitué
    Profil pro
    Inscrit en
    Mai 2002
    Messages
    958
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2002
    Messages : 958
    Points : 141
    Points
    141
    Par défaut usage ou non usage du mot cle YIELD dans une boucle foreach
    Bonjour,

    La question que j'ai aujourd'hui concerne la différence entre l'usage du moit clé YIELD dans une boucle foreach et son non usage dans ce même type de boucle.


    D'après ce que je connais , chaque interface IEnumerable<T>, doit définir une méthode GetEnumerator()
    Cette méthode retourne un objet qui implémente l'interface IEnumerator<T>.

    Cet objet retourné fournit la logique dont a besoin l'instruction foreach.

    Cette instruction foreach fournit donc l'énumérateur par défaut dont on a besoin pour le parcours d'une collection.

    voici un exemple de boucle foreach


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
     
    class CustomCollectionClass<T> : IEnumerable<T>{
     
    public IEnumerator<T>GetEnumerator(){
    ......
    }
     
    CustomCollectionCalss<int> intCollection= new CustomCollectionClass<int>();
    intCollection.Add(3);
    intCollection.Add(5);
    intCollection.Add(8);
    intCollection.Add(2);
    //la boucle foreach entraîne l'usage implicite de  l'enumerateur par défaut
    foreach (int temp in intCollection){
     
    }
     
    }
    Je peux donc considérer qu'à chaque fois que j'utilise la boucle foreach, j'utilise implicitement un objet de type IEnumerator qui contient les méthodes MoveNext et Reset ainsi que la property Current.

    Par ailleurs, j'apprends que si j'utilise mot clé yield dans une boucle for, ainsi

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
     
    class BasicCollection<T>:IEnumerable<T>{
     
    private List<T> data = new List<T>;
     
    public void FillList(params T[]items){
     
    foreach(var datum in items)
    data.Add(datum);
     
    }
     
    IEnumerator<T>IEnumerable<T>.GetEnumerator(){
    foreach (var datum in data){
     
    yield return datum;
     
     
    }
     
    }
    Dans ce cas, la méthode GetEnumerator()ne retourne pas un objet de type IEnumerator<T> ,c'est à dire qui implémente les méthodes MoveNext et Reset ainsi que retourne la property Current(Ah bon!) .
    A la place elle boucle au niveau des éléménts de le collection data et retourne chaque élement l'un après l'autre(j'avoue que je ne vois pas la différence avec le fait d'appeler implicitement les méthodes moveNext et utiliser la property Current).

    C'est le mot clé YIELD qui va permettre ce retour d'un objet de type IEnumerator<T>.
    J'avoue que cela contredit ce que j'ai écris précédemment concernant l'usage d'un enumateur par défaut dans une boucle foreach

    En effet,j'apprends que par l'usage du mot clé YIELD :
    -la valeur concernée par chaque itération est renvoyée donc cela implique l'usage implicite de la property Current

    -le passage à la prochaine valeur s'effectue grâce à l'appel implicite de la méthode MoveNext()

    Le mot clé YIELD est utilisé par le compilateur pour générr une implémentation de l'interface IEnumerator<T< qui contient les méthodes MoveNext() et Reset()
    ainsi que la property Current.

    J'avoue que je ne vois pas la différence entre une simple boucle foreach , qui, si j'ai bien compris, utilise implicitement un objet de type IEnumerator<T> et l'usage du mot clé YIELD dans une boucle foreach pour boucler au niveau des éléments d'une collection.

    Je vous remercie beaucoup de bien vouloir m'expliquer la différence exacte entre l'usage et le non usage de ce mot clé dans une boucle foreach.

    Bien cordialement.

    new_wave
    Il vaut mieux mobiliser son intelligence sur des conneries que mobiliser sa connerie sur des choses intelligentes. [SHADOKS]

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 749
    Points
    39 749
    Par défaut
    La façon dont ta question est posée n'a pas beaucoup de sens, sans doute parce que tu t'es un peu emmêlé les pinceaux... Je vais donc y répondre indirectement, en essayant d'expliquer le sens de yield return.

    En fait, le principe d'un itérateur (c'est-à-dire une méthode qui utilise yield return) est que le compilateur va réécrire le code de façon à ce que la méthode renvoie en fait un objet (généré par le compilateur) qui implémente IEnumerator<T> (ou IEnumerable<T>, car on peut déclarer l'un où l'autre comme type de retour d'un itérateur) et renvoie les éléments en suivant la logique de la méthode. C'est juste une facilité d'écriture pour éviter d'avoir à écrire manuellement un objet IEnumerator<T> ou IEnumerable<T>.

    Par exemple, si tu as l'itérateur suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    IEnumerable<int> GetNumbers()
    {
        yield return 42;
        yield return 123;
        yield return 999;
    }
    Le compilateur va réécrire ça de la façon suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    IEnumerable<int> GetNumbers()
    {
        return new GetNumbersEnumerable();
    }
    La classe GetNumbersEnumerable (en réalité c'est un nom un peu plus "bizarre" que ça, mais pour l'explication c'est plus simple comme ça) est générée par le compilateur et ressemble à quelque chose comme ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    class GetNumbersEnumerable : IEnumerable<int>
    {
        public IEnumerator<int> GetEnumerator()
        {
            return new GetNumbersEnumerator();
        }
     
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
     
        class GetNumbersEnumerator : IEnumerator<int>
        {
            private int _current;
            private int _state;
            public bool MoveNext()
            {
                switch (_state)
                {
                    case 0:
                        _current = 42;
                        _state++;
                        return true;
                    case 1:
                        _current = 123;
                        _state++;
                        return true;
                    case 2:
                        _current = 999;
                        _state++;
                        return true;
                    default:
                        return false;
                }
            }
     
            public int Current
            {
                get { return _current; }
            }
     
            public void Dispose()
            {
            }
     
            public void Reset()
            {
                _state = 0;
            }
     
            object IEnumerator.Current
            {
                get { return Current; }
            }
        }
    }
    La classe GetNumbersEnumerator implémente en fait dans la méthode MoveNext un automate fini qui reproduit la logique du code de ta méthode GetNumbers d'origine. Ici c'est un cas très simple, donc le code de MoveNext est facilement compréhensible, mais pour des cas plus compliqués ça génère un code assez peu lisible à base de switch et de goto, pour pourvoir reprendre là où ça en était...

    Donc en gros, même si toi, tu ne renvoies pas explicitement un objet IEnumerator<T>, le compilateur s'en charge pour toi

    Plus d'infos sur MSDN : Itérateurs

  3. #3
    Membre habitué
    Profil pro
    Inscrit en
    Mai 2002
    Messages
    958
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2002
    Messages : 958
    Points : 141
    Points
    141
    Par défaut
    Bonjour et merci encore beaucoup de ta réponse.

    Ce que j'en comprends, c'est qu'au lieu d'implémenter par nous mêmes les méthodes MoveNext ()ou Reset() ou encore de récupérer la valeur courante par le getter de la property Current, on utilise une instruction rde type yield return ;

    Ce que je ne comprends pas bien, c'est quelle différence existe-til entre l'usage d'une boucle foreach et l'usage d'une méthode qui contient une instruction return yield.
    En effet, avec la boucle foreach comme avec l'instruction yield return , le compilateur implémente par défaut in itérateur c'est à dire, comme tu me l'as expliqué , implemente une méthode MoveNext, une property current et une méthode Reset().


    Par ailleurs, j'aimerais poser deux questions simples.

    Quand j'utilise une boucle foreach dans une classe, je ne crée pas forcément de méthode GetEnumerator() qui implémente du code qui sera executé à chaque boucle foreach.
    Est-ce du fait que la classe fait appel , par défaut, à une méthode GetEnumerator, qui utilise un itérateur par défaut?



    Pour les classes de collection qui implémentent IEnumerable<T>.

    Si je souhaite parcourir une collection de ce type avec une boucle foreach et en utilisant l'énumérateur par défaut, suis je obligé d'implémenter la méthode GetEnumerator().

    Pour finir, je te remercie beaucoup de confirmer ou d'infirmer ce que j'écris et de bien vouloir me corriger si besoin est.

    Si je ne suis pas obligée d'implémenter la méthode GetEnumerator()par ce que je souhaite utiliser l'itérateur par défaut, je comprends alors qu'on définit nous-mêmes la méthode GetEnumerator et on utilise

    le mot clé yield , si dans une boucle foreach, on ne souhaite pas faire appel à l'énumérateur par défaut.
    L'usage du mot clé YIELD remplace l'appel de la méthode MoveNext(), ainsi que la récupération de la valeur courante d'une collection, par l'appel du getter de la property Current.


    Merci encore beaucoup de ton aide pour m'aider à bien voir clair sur ce sujet.

    Bien cordialement.

    new_wave
    Il vaut mieux mobiliser son intelligence sur des conneries que mobiliser sa connerie sur des choses intelligentes. [SHADOKS]

  4. #4
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 749
    Points
    39 749
    Par défaut
    Citation Envoyé par new_wave Voir le message
    Ce que je ne comprends pas bien, c'est quelle différence existe-til entre l'usage d'une boucle foreach et l'usage d'une méthode qui contient une instruction return yield.
    En effet, avec la boucle foreach comme avec l'instruction yield return , le compilateur implémente par défaut in itérateur c'est à dire, comme tu me l'as expliqué , implemente une méthode MoveNext, une property current et une méthode Reset().
    Ca n'a rien à voir... foreach sert à consommer une séquence, alors que yield return sert à générer une séquence. Bien sûr, rien n'empêche de combiner les deux : tu peux générer une séquence au fur et à mesure que tu en consommes une autre, c'est d'ailleurs un pattern assez fréquent.

    Citation Envoyé par new_wave Voir le message
    Quand j'utilise une boucle foreach dans une classe, je ne crée pas forcément de méthode GetEnumerator() qui implémente du code qui sera executé à chaque boucle foreach.
    Est-ce du fait que la classe fait appel , par défaut, à une méthode GetEnumerator, qui utilise un itérateur par défaut?
    Je ne comprends pas très bien le scénario que tu décris... Pour pouvoir faire foreach (var x in quelquechose), il faut que quelquechose ait une méthode GetEnumerator. Si c'est toi qui écris le code de quelquechose, il faut effectivement que tu implémentes cette méthode, sinon tu te contentes de l'utiliser (implicitement, puisque c'est le compilateur qui s'occupe d'appeler GetEnumerator)


    Citation Envoyé par new_wave Voir le message
    Pour les classes de collection qui implémentent IEnumerable<T>.

    Si je souhaite parcourir une collection de ce type avec une boucle foreach et en utilisant l'énumérateur par défaut, suis je obligé d'implémenter la méthode GetEnumerator().
    Bah si la classe implémente IEnumerable<T>, il y a déjà une méthode GetEnumerator, donc tu n'as pas à l'implémenter.
    Et je ne vois pas trop ce que tu appelles "énumérateur par défaut"...

    Citation Envoyé par new_wave Voir le message
    Si je ne suis pas obligée d'implémenter la méthode GetEnumerator()par ce que je souhaite utiliser l'itérateur par défaut, je comprends alors qu'on définit nous-mêmes la méthode GetEnumerator et on utilise

    le mot clé yield , si dans une boucle foreach, on ne souhaite pas faire appel à l'énumérateur par défaut.
    L'usage du mot clé YIELD remplace l'appel de la méthode MoveNext(), ainsi que la récupération de la valeur courante d'une collection, par l'appel du getter de la property Current.
    Je comprends pas grand chose à ton histoire. L'usage de yield ne "remplace" pas l'appel à MoveNext : au contraire, il crée la méthode MoveNext, selon le mécanisme décrit dans mon précédent message.

    Je répète ce que j'ai dit au début de ce message, parce que je pense que c'est ce point là que tu n'as pas bien compris : foreach sert à consommer une séquence, alors que yield return sert à générer une séquence.

  5. #5
    Membre régulier
    Inscrit en
    Avril 2010
    Messages
    200
    Détails du profil
    Informations forums :
    Inscription : Avril 2010
    Messages : 200
    Points : 111
    Points
    111
    Par défaut
    Bonjour,

    Je me joins à la conversation mais pour poser une question simple car au final vous utilisez beaucoup de mots et d'explications compliquées pour simplement expliquer que yield sert à générer une séquence comme tu l'as dit Tomlev.

    Ma question :

    Quelle solution est la mieux à utiliser ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    foreach (string current in list)
    {
        yield return current;
    }
    ou bien
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    List<string> listReturned = new List<string>();
    foreach (string current in list)
    {
        listReturned.Add(current);
    }
    return listReturned;
    ?

    Pour l'avoir utilisé récemment, en deboguant j'avais l'impression qu'avec le yield, ma méthode (contenant le yield) pouvait être appelée plusieurs fois.
    Par exemple une fois lors de l'appel pour récupérer une liste, une autre fois pour faire un simple count et ainsi de suite.

  6. #6
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 749
    Points
    39 749
    Par défaut
    Citation Envoyé par Air P-E Voir le message
    Quelle solution est la mieux à utiliser ?
    Ca dépend de ce que tu veux faire, mais en gros, la principale différence est qu'avec yield return, l'exécution est différée. Je veux dire par là qu'au moment où tu appelles la méthode, tu récupères un IEnumerable ou IEnumerator, mais le corps de l'itérateur n'est pas exécuté tant que tu ne commences pas à énumérer le résultat. Et quand tu énumères, la collection source est énumérée au fur et à mesure.

    Alors que si tu remplis une liste et que tu la renvoies, l'énumération est faite immédiatement lors de l'appel de la méthode, et le fait que tu énumère la liste ou non n'a pas d'effet sur la méthode qui la crée.

    Citation Envoyé par Air P-E Voir le message
    Pour l'avoir utilisé récemment, en deboguant j'avais l'impression qu'avec le yield, ma méthode (contenant le yield) pouvait être appelée plusieurs fois.
    Par exemple une fois lors de l'appel pour récupérer une liste, une autre fois pour faire un simple count et ainsi de suite.
    Si tu as une méthode comme ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    public IEnumerable<int> GetNumbers()
    {
        foreach(int i in list)
        {
            yield return i;
        }
    }
    et que tu l'appelles plusieurs fois en faisant ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    int count = GetNumbers().Count();
    foreach(var num in GetNumbers())
    {
        ...
    }
    Elle va effectivement être exécutée plusieurs fois, mais c'est logique, puisque tu l'as appelée plusieurs fois. Ce qui est un peu moins évident à comprendre, c'est que même si tu fais ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    var numbers = GetNumbers();
    int count = numbers.Count();
    foreach(var num in numbers)
    {
        ...
    }
    ton bloc itérateur sera également exécuté 2 fois. En effet, ce qui déclenche son exécution n'est pas l'appel de la méthode, mais l'énumération du résultat. Ici le résultat est énuméré 2 fois, donc l'itérateur est exécuté 2 fois. Pour éviter ça, il faut "matérialiser" le résultat, en appelant par exemple ToList ou ToArray :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    var numbers = GetNumbers().ToList();
    int count = numbers.Count();
    foreach(var num in numbers)
    {
        ...
    }

  7. #7
    Membre régulier
    Inscrit en
    Avril 2010
    Messages
    200
    Détails du profil
    Informations forums :
    Inscription : Avril 2010
    Messages : 200
    Points : 111
    Points
    111
    Par défaut
    Pour cette partie du code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    int count = GetNumbers().Count();
    foreach(var num in GetNumbers())
    {
        ...
    }
    Effectivement on l'appelle deux fois je suis d'accord, c'était sur ce type de code que j'avais pu observer ce phénomène :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    var numbers = GetNumbers();
    int count = numbers.Count();
    foreach(var num in numbers)
    {
        ...
    }
    Ok je comprends mieux pour la notion matérialisation. Un peu comme à la manière de ReSharper des fois, qui propose en gros de transformer un IEnumerable en Array.
    Donc si je comprends bien, le fait de ne pas "matérialiser" n'est pas une solution efficace en terme de temps d'exécution ?
    Tu préconises quelle solution ?

    Ca dépend de ce que tu veux faire, mais en gros, la principale différence est qu'avec yield return, l'exécution est différée. Je veux dire par là qu'au moment où tu appelles la méthode, tu récupères un IEnumerable ou IEnumerator, mais le corps de l'itérateur n'est pas exécuté tant que tu ne commences pas à énumérer le résultat. Et quand tu énumères, la collection source est énumérée au fur et à mesure.
    Tu aurais des exemples de cas fonctionnel/technique par hasard ?

    En tout cas merci de ta réponse.

    EDIT : Pour le moment le seul cas que je vois (et encore je suis même pas sûr tu vas peut-être me contredire) où c'est intéressant, c'est lorsque que tu as des listes énorme à retourner. Au lieu de créer une liste temporaire que tu renvoies, tu retournes les éléments au fur et à mesure.
    Après ce type d'utilisation peut se faire sur toutes les méthodes de ce type du coup ? Il y a des inconvénients ?

  8. #8
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 749
    Points
    39 749
    Par défaut
    Citation Envoyé par Air P-E Voir le message
    Donc si je comprends bien, le fait de ne pas "matérialiser" n'est pas une solution efficace en terme de temps d'exécution ?
    Tu préconises quelle solution ?
    Bah ça dépend... Si tu peux n'énumérer la séquence qu'une seule fois, c'est l'idéal (car l'énumération peut potentiellement être couteuse, par exemple si c'est une requête Entity Framework qui va exécuter une requête SQL sur le serveur). Sinon, si le volume de données n'est pas trop important, tu peux matérialiser avec ToList et travailler là-dessus.

    Citation Envoyé par Air P-E Voir le message
    Tu aurais des exemples de cas fonctionnel/technique par hasard ?
    Par exemple si tu fais une fonction qui renvoie les lignes d'un fichier :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    static IEnumerable<string> ReadLines(string fileName)
    {
        using (var reader = new StreamReader(fileName))
        {
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                yield return line;
            }
        }
    }
    Si tu fais un foreach sur le résultat de ReadLines, le fichier est lu au fur et à mesure que tu énumères. Si tu sors de ton foreach au bout de 2 itérations, les lignes suivantes ne sont jamais lues, et le fichier est fermé. Le comportement d'un itérateur est donc "lazy", dans le sens où il fait rien avant que ce soit absolument nécessaire.

    Citation Envoyé par Air P-E Voir le message
    EDIT : Pour le moment le seul cas que je vois (et encore je suis même pas sûr tu vas peut-être me contredire) où c'est intéressant, c'est lorsque que tu as des listes énorme à retourner. Au lieu de créer une liste temporaire que tu renvoies, tu retournes les éléments au fur et à mesure.
    Après ce type d'utilisation peut se faire sur toutes les méthodes de ce type du coup ? Il y a des inconvénients ?
    Non, il y a plein d'autres cas... Regarde l'exemple que je viens de donner par exemple : si tu avais utilisé Files.ReadAllLines, tu aurais récupéré un tableau avec toutes les lignes du fichier, ce qui pourrait être gênant si le fichier est très grand, alors qu'avec l'itérateur ReadLines qui renvoie un IEnumerable, tu peux traiter les lignes une par une, et tu n'as même pas besoin de lire le fichier jusqu'au bout.

    D'une manière générale, les itérateurs sont très utiles pour faire des traitements "à la volée" sur des collections. Une grande partie des opérateurs Linq (Where, Select, etc) sont implémentés comme ça, ou pourraient l'être. Jette un oeil à la série Edulinq de Jon Skeet, où il montre comment réimplémenter les opérateurs Linq ; il utilise yield return un peu partout.

  9. #9
    Membre régulier
    Inscrit en
    Avril 2010
    Messages
    200
    Détails du profil
    Informations forums :
    Inscription : Avril 2010
    Messages : 200
    Points : 111
    Points
    111
    Par défaut
    Merci tomlev pour ta réponse, je comprends mieux le fonctionnement maintenant.
    Il faudra que je me repenche sur le point dans mon projet du coup.
    Je ne sais pas où est passé l'auteur du post mais j'espère qu'il a trouvé ses réponses.

  10. #10
    Membre éprouvé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2006
    Messages
    436
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2006
    Messages : 436
    Points : 963
    Points
    963
    Par défaut
    Pouaaa j'en ai appris en vous lisant ! Pour moi, yield, c'était un mot clé pour se la péter ou faire genre je suis un crack du .NET

    Même si c'est un petit peu confus pour l'instant, j'ai compris l'essentiel. Et super l'exemple de lecture de fichier ! Ca parle tout seul

    Donc ce post est super instructif ! Merci Tom !
    "S'adapter, c'est vaincre" - Cellendhyll de Cortavar

  11. #11
    Membre habitué
    Profil pro
    Inscrit en
    Mai 2002
    Messages
    958
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2002
    Messages : 958
    Points : 141
    Points
    141
    Par défaut usage de YIELD
    Merci également beaucoup de toutes ces informations qui m'ont bien aidée à mieux comprendre la différence entre l'usage de yield et le simple usage de foreach.

    Pourrais tu juste revenir sur ce que tu as écris
    Je veux dire par là qu'au moment où tu appelles la méthode, tu récupères un IEnumerable ou IEnumerator, mais le corps de l'itérateur n'est pas exécuté tant que tu ne commences pas à énumérer le résultat. Et quand tu énumères, la collection source est énumérée au fur et à mesure.

    Alors que si tu remplis une liste et que tu la renvoies, l'énumération est faite immédiatement lors de l'appel de la méthode, et le fait que tu énumère la liste ou non n'a pas d'effet sur la méthode qui la crée.
    Pourrais-tu m'expliquer ce que cela signifie exactement
    mais le corps de l'itérateur n'est pas exécuté tant que tu ne commences pas à énumérer le résultat.
    Qu'est ce que le corps de l'itérateur?
    que signifie "énumérer le résultat"
    puis ensuite

    Et quand tu énumères, la collection source est énumérée au fur et à mesure.
    Je pense avoir bien compris ce qui se passe avec yield mais c'est l'usage des notions écrites ci-dessus qui me bloquent un peu.

    Merci encore beaucoup à toi.

    new_wave
    Il vaut mieux mobiliser son intelligence sur des conneries que mobiliser sa connerie sur des choses intelligentes. [SHADOKS]

  12. #12
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 749
    Points
    39 749
    Par défaut
    Citation Envoyé par new_wave Voir le message
    Qu'est ce que le corps de l'itérateur?
    L'itérateur, c'est la méthode qui contient yield, et qui est transformée par le compilateur en une classe qui implémente IEnumerable et/ou IEnumerator, comme expliqué plus haut. Puisque la logique de la méthode se retrouve dans la méthode MoveNext de la classe générée par le compilateur, elle n'est exécutée que quand on appelle MoveNext.

    Citation Envoyé par new_wave Voir le message
    que signifie "énumérer le résultat"
    Récupérer le contenu de la séquence, par exemple en utilisant une boucle foreach, ou alors en appelant manuellement GetEnumerator() puis MoveNext

    Citation Envoyé par new_wave Voir le message
    puis ensuite
    Et quand tu énumères, la collection source est énumérée au fur et à mesure.
    Je veux dire que dans un itérateur qui se base sur une collection source, on n'avance à l'élément suivant de la source que quand le consommateur de l'itérateur avance à l'élément suivant.

    C'est un peu plus facile à comprends avec l'exemple ReadLines que j'ai donné plus haut (ce n'est pas une collection mais un fichier, mais le principe est le même) : quand tu boucles sur le résultat de ReadLines, le fichier est lu au fur et à mesure. A chaque fois que tu fais MoveNext (soit explicitement, soit avec un foreach), une ligne du fichier est lue, mais pas plus. Il n'est pas nécessaire de lire tout le fichier tout de suite, on le lit seulement au fur et à mesure que c'est nécessaire pour fournir le résultat suivant.

    Je suis pas sûr que ce soit très clair, mais c'est pas évident à expliquer... une fois que tu l'as compris, ce n'est finalement pas un concept très compliqué, c'est juste difficile de trouver les mots pour le décrire...

  13. #13
    Membre habitué
    Profil pro
    Inscrit en
    Mai 2002
    Messages
    958
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2002
    Messages : 958
    Points : 141
    Points
    141
    Par défaut generer et consommer une séquence
    Bonjour et merci de ces explications que j'ai relues et me semble avoir mieux comprises.

    Une question : Quand le compilateur tombe sur l'instruction yield return , peut on considérer qu'il alimente , à chaque yield return le contenu de la méthode MoveNext, en ajoutant une case dans le switch tel que tu l'as montré dans le code de ta première réponse.
    Puis ensuite, le foreach "consomme" ce qui a été généré par le yied return et retourne la valeur de la variable _current(ceci se fait de manière implicite).

    Te remerciant de ta réponse.

    Bien cordialement.

    new_wave
    Il vaut mieux mobiliser son intelligence sur des conneries que mobiliser sa connerie sur des choses intelligentes. [SHADOKS]

  14. #14
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 749
    Points
    39 749
    Par défaut
    Oui, grosso modo c'est ça qui se passe

  15. #15
    Membre habitué
    Profil pro
    Inscrit en
    Mai 2002
    Messages
    958
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2002
    Messages : 958
    Points : 141
    Points
    141
    Par défaut
    Bonjour,

    Merci à toi.
    Dernière question : lorsque je dis qu'à chaque yield return, le compilateur "alimente" le code de la méthode MoveNext(), peut on considérer qu'il recrée une nouvelle méthode MoveNext à chaque yield return et donc un nouvel objet de type IEnumerator.

    Te remerciant encore de ta réponse.
    Cordialement.

    new_wave
    Il vaut mieux mobiliser son intelligence sur des conneries que mobiliser sa connerie sur des choses intelligentes. [SHADOKS]

  16. #16
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 749
    Points
    39 749
    Par défaut
    Citation Envoyé par new_wave Voir le message
    Dernière question : lorsque je dis qu'à chaque yield return, le compilateur "alimente" le code de la méthode MoveNext(), peut on considérer qu'il recrée une nouvelle méthode MoveNext à chaque yield return et donc un nouvel objet de type IEnumerator.
    Non, il n'y a qu'une seule méthode MoveNext ; chaque yield return correspond à un case dans le switch de la méthode générée.

  17. #17
    Membre habitué
    Profil pro
    Inscrit en
    Mai 2002
    Messages
    958
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2002
    Messages : 958
    Points : 141
    Points
    141
    Par défaut
    Bonjour et merci de ta réponse.

    Je considère que le sujet est résolu.

    Bien cordialement et avec tous mes remerciements pour ces échanges fructueux.

    curieuse_prog
    Il vaut mieux mobiliser son intelligence sur des conneries que mobiliser sa connerie sur des choses intelligentes. [SHADOKS]

  18. #18
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 749
    Points
    39 749
    Par défaut
    Citation Envoyé par new_wave Voir le message
    Bien cordialement et avec tous mes remerciements pour ces échanges fructueux.
    avec une looooongue pause au milieu

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Mot Cle "N" dans ma requete SQL
    Par rgarnier dans le forum SQL
    Réponses: 12
    Dernier message: 21/07/2009, 11h09
  2. Desactiver mot-cle Date dans un Insert
    Par nuriel2 dans le forum C#
    Réponses: 2
    Dernier message: 19/10/2007, 15h58
  3. mot cle invisible dans l'index(workshop help html)
    Par bbelle08 dans le forum Balisage (X)HTML et validation W3C
    Réponses: 9
    Dernier message: 17/08/2007, 09h32
  4. Réponses: 2
    Dernier message: 22/02/2006, 11h18
  5. sécuriser le mot de passe dans une page asp
    Par Redouane dans le forum ASP
    Réponses: 2
    Dernier message: 10/03/2004, 21h16

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo