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

C# Discussion :

Bug du framework sur résolution d'un IEnumerable ? Ce qui se passe ligne 13 va vous étonner !!!


Sujet :

C#

  1. #1
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut Bug du framework sur résolution d'un IEnumerable ? Ce qui se passe ligne 13 va vous étonner !!!
    Bonjour,

    désolé pour le titre, je me suis inspiré de ce qui ce fait de mieux sur le net (mettez un pouce bleu et lachez des com )

    Je sais bien que les IEnumerable sont "exécutés" à l'utilisation, mais là j'ai un collègue qui a un cas vraiment étrange, et je veux bien l'éclaircissement du comment du pourquoi
    reproduit avec le code simplifié suivant :

    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
     public Form1()
            {
                InitializeComponent();
                var lb = Test(); 
                var s = lb.ToList()[0].prop1; // fin du test, s contient "test" au lieu de contenir "test changé"
            }
     
            internal IEnumerable<B> Test()
            {
                List<A> la = new List<A>();
                la.Add(new A() { prop1 = "test" });
                IEnumerable<B> lb = la.Select(a => transform(a));
                lb.ToList().ForEach(b => b.prop1 += " changé");
                return lb;
            }
     
            private B transform(A a)
            {
                return new B(a.prop1);
            }
            internal class A
            {
                public string prop1 { get; set; }
            }
            internal class B
            {
                public B(string s)
                {
                    this.prop1 = s;
                }
                public string prop1 { get; set; }
            }
    sachant qu'on a gratté un peu on a trouvé d'où ca venait
    tout se passe ici, en déplacant le .ToList une ligne au dessus prop1 contiendra bien "test changé"
    List<B> lb = la.Select(a => transform(a)).ToList;
    lb.ForEach(b => b.prop1 += " changé");

    alors qu'on a mis un point d'arrêt dans le set de prop1, on y passe bien, la variable privée est bien settée, mais en sortie on avait pas la bonne valeur ...
    (VS2015 fx 4.6.1)

    ca me parait être plus un bug, donc je veux bien l'explication si c'est une feature ^^

    merci.
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  2. #2
    Expert éminent sénior

    Avatar de François DORIN
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Juillet 2016
    Messages
    2 757
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Charente Maritime (Poitou Charente)

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

    Informations forums :
    Inscription : Juillet 2016
    Messages : 2 757
    Points : 10 697
    Points
    10 697
    Billets dans le blog
    21
    Par défaut
    Bonjour,

    Alors, ce n'est pas les IEnumerable qui sont exécutés à l'utilisation, mais les valeurs de retour des méthodes qui sont évaluées à l'utilisation. Bon, c'est de la sémantique, mais le problème était bien là où tu le penses.

    Le soucis vient de cette portion de code :
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    IEnumerable<B> lb = la.Select(a => transform(a));
    lb.ToList().ForEach(b => b.prop1 += " changé");
    return lb;

    Et plus particulièrement, de la seconde ligne.

    • tu initialises lb avec le résultat de la.Select. Jusque là, rien d'anormal. La méthode transform n'est pas encore appelée ;
    • ligne suivante, tu commences par faire un lb.ToList(). Donc là, il s'agit de créer une liste à partir de lb. Lb est donc évaluée (donc transform) afin de générer la liste ;
    • ensuite, tu appliques la méthode ForEach. Comme pour le Select, elle ne sera évaluée que lorsque cela sera nécessaire ;
    • tu retournes lb. C'est là qu'est le piège. L'instance IEnumerable créée précédemment via l'appel à ForEach n'est plus utilisée. Elle ne sera donc pas évaluée, et les objets ne seront donc pas modifiés !


    Réécrivons légèrement la portion de code
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    IEnumerable<B> lb = la.Select(a => transform(a));
    List<B> list = lb.ToList();
    list.ForEach(b => b.prop1 += " changé");
    return list;

    Il fait exactement la même chose, mais renvoie la dernière collection, et non celle issue du Select. Et là, ça marchera
    François DORIN
    Consultant informatique : conception, modélisation, développement (C#/.Net et SQL Server)
    Site internet | Profils Viadéo & LinkedIn
    ---------
    Page de cours : fdorin.developpez.com
    ---------
    N'oubliez pas de consulter la FAQ C# ainsi que les cours et tutoriels

  3. #3
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    ok, plutot convaincant

    si j'ai bien compris lb contient juste le fait que c'est un select de la, et en retournant lb je ne fais que retourner le select de la

    par contre un détail, le code du for each est bien exécuté malgré le fait qu'on ne l'évalue pas (on a mis un point d'arret dans la lambda)
    mais vu qu'il est sur lb.ToList() j'imagine que c'est l'instance de lb.ToList() que je n'ai rangé dans rien qui a subit les changements

    merci bien !
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  4. #4
    Expert éminent sénior

    Avatar de François DORIN
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Juillet 2016
    Messages
    2 757
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Charente Maritime (Poitou Charente)

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

    Informations forums :
    Inscription : Juillet 2016
    Messages : 2 757
    Points : 10 697
    Points
    10 697
    Billets dans le blog
    21
    Par défaut
    Citation Envoyé par Pol63 Voir le message
    si j'ai bien compris lb contient juste le fait que c'est un select de la, et en retournant lb je ne fais que retourner le select de la
    C'est tout à fait ça

    Citation Envoyé par Pol63 Voir le message
    par contre un détail, le code du for each est bien exécuté malgré le fait qu'on ne l'évalue pas (on a mis un point d'arret dans la lambda)
    mais vu qu'il est sur lb.ToList() j'imagine que c'est l'instance de lb.ToList() que je n'ai rangé dans rien qui a subit les changements
    Oui, là c'est moi qui divague... Les méthodes styles Where, Select, (qui permettent un chaînage) ne sont évaluées que lorsque c'est nécessaire. Mais le ForEach lui est évaluée immédiatement.

    Lorsque lb est renvoyé, ce qui est renvoyé, en gros, ce n'est pas le résultat du Select, mais le Select lui-même. Aussi, lorsque tu as besoin d'accéder au résultat, il est recalculé (d'où le ForEach sans effet, puisque non appelé !). Exactement ce que tu disais donc.

    Une bonne pratique, pour éviter justement ce genre d'écueils, est de toujours terminer une requête par un ToList() ou un ToArray()
    François DORIN
    Consultant informatique : conception, modélisation, développement (C#/.Net et SQL Server)
    Site internet | Profils Viadéo & LinkedIn
    ---------
    Page de cours : fdorin.developpez.com
    ---------
    N'oubliez pas de consulter la FAQ C# ainsi que les cours et tutoriels

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

Discussions similaires

  1. Probléme étrange sur l'allocation mémoire
    Par yann458 dans le forum C++
    Réponses: 14
    Dernier message: 18/07/2014, 14h07
  2. [Tableaux] probléme étrange sur un array
    Par boadog dans le forum Langage
    Réponses: 2
    Dernier message: 28/11/2007, 10h43
  3. Réponses: 5
    Dernier message: 12/07/2007, 10h07
  4. [xp] problème étrange sur le système de fichiers
    Par Huntress dans le forum Windows XP
    Réponses: 4
    Dernier message: 05/03/2006, 20h15
  5. [MFC] Problème pointeur sur une classe
    Par mick74 dans le forum MFC
    Réponses: 7
    Dernier message: 14/04/2004, 14h17

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