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 :

[3.5] List<T>.Count - Bug du framework connu ?


Sujet :

C#

  1. #1
    Membre habitué Avatar de thelpi
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    217
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Deux Sèvres (Poitou Charente)

    Informations forums :
    Inscription : Décembre 2007
    Messages : 217
    Points : 156
    Points
    156
    Par défaut [3.5] List<T>.Count - Bug du framework connu ?
    Bonjour à tous,

    Je viens de détecter un bug qui me parait hallucinant, je vous laisse juger :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    int countLines = 1;
    foreach (DocLigne line in doc.LstDocLigne)
    {
            // AddLog affiche à l'écran le message précisé en paramètre
            AddLog("Traitement... (ligne " + countLines + " de " + doc.LstDocLigne.Count + ")");
     
            // traitement sur la ligne, QUI NE MODIFIE PAS LA COLLECTION
     
            countLines++;
    }
    En résultat à l'écran, j'ai eu ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    // Traitement... (ligne 1 de 21)
    // Traitement... (ligne 2 de 21)
    // ...
    // Traitement... (ligne 21 de 21)
    // Traitement... (ligne 22 de 21)
    // Traitement... (ligne 23 de 21)
    Que faut-il en conclure ? La propriété Count de List<T> n'est pas fiable ? ou ai-je un bug que je n'aurais pas vu ?
    Sachant que la collection parcourue avait bien 23 éléments.

    Merci.

  2. #2
    Membre émérite
    Profil pro
    Mangeur de gauffre
    Inscrit en
    Octobre 2007
    Messages
    4 413
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Mangeur de gauffre

    Informations forums :
    Inscription : Octobre 2007
    Messages : 4 413
    Points : 2 498
    Points
    2 498
    Par défaut
    Salut

    Il y a certainement un bug que tu n'a pas vu

    2 remarques
    1- La boucle
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    for (int countlines=0;countlines<doc.LstDocLigne.Count;countlines++)
    {
      AddLog(String.Format("Traitement... (ligne {0} de {1} ",countLines ,doc.LstDocLigne.Count );
    }
    est plus appropriée a ce que tu fais qu'un foreach

    2- J'ai aussi amélioré ta construction

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    AddLog(String.Format("Traitement... (ligne {0} de {1} ",countLines ,c.LstDocLigne.Count );

  3. #3
    Membre habitué Avatar de thelpi
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    217
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Deux Sèvres (Poitou Charente)

    Informations forums :
    Inscription : Décembre 2007
    Messages : 217
    Points : 156
    Points
    156
    Par défaut
    Merci pour la réponse,

    en fait, l'amélioration que tu proposes me parait pire (sans offenses)
    Si le Count n'est pas fiable, faire une boucle sur l'incrément à la place du foreach va empirer les choses, non? Au moins avec le foreach je suis sur de boucler sur les 23 lignes et pas 21.

    Pour info, j'ai dis une petite erreur, qui a peut être son importance :
    le résultat à l'écran était le suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    // Traitement... (ligne 1 de 23)
    // Traitement... (ligne 2 de 23)
    // ...
    // Traitement... (ligne 5 de 23)
    // Traitement... (ligne 6 de 21)
    // ...
    // Traitement... (ligne 21 de 21)
    // Traitement... (ligne 22 de 21)
    // Traitement... (ligne 23 de 21)
    J'ai rien trouvé de suspect en relisant mon code (qui a marché très bien pendant 2 mois par ailleurs), même si je n'exclue pas une erreur bien entendue !

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Février 2003
    Messages
    311
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2003
    Messages : 311
    Points : 337
    Points
    337
    Par défaut
    Tu dois avoir une erreur dans ton code, ça se serait vu depuis longtemps un tel bug (mais on est sûr de rien )

  5. #5
    Membre émérite
    Profil pro
    Mangeur de gauffre
    Inscrit en
    Octobre 2007
    Messages
    4 413
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Mangeur de gauffre

    Informations forums :
    Inscription : Octobre 2007
    Messages : 4 413
    Points : 2 498
    Points
    2 498
    Par défaut
    Mais bien sur que le count est fiable

    Si tu veux envoyer le code de toute ta boucle, je te promet qu'on trouve
    l'erreur !
    En esperant quand meme que ta boucle n'appelle pas trop de methode

    AddLog(" etc..

    Susceptible de modifier ta liste

  6. #6
    Membre habitué Avatar de thelpi
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    217
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Deux Sèvres (Poitou Charente)

    Informations forums :
    Inscription : Décembre 2007
    Messages : 217
    Points : 156
    Points
    156
    Par défaut
    En résumé (je ne peux pas mettre le code réel) ça ressemble fortement à ça, les requêtes paramétrés en moins :

    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
     
    string NoFacture = GetNewFactureNo();
    CreateEnteteFacture(NoFacture);
     
    int countLines = 1;
    foreach (DocLigne line in doc.LstDocLigne)
    {
            // AddLog affiche à l'écran le message précisé en paramètre
            AddLog("Traitement... (ligne " + countLines + " de " + doc.LstDocLigne.Count + ")");
     
           string queryAddFALine = "insert into facture_lines (date, numero, line, article) values ('" doc.Date + "','" + NoFacture + "'," + line.No + ",'" + line.Article "')";
    string queryDelBlLine = "delete from bon_livraison_lines where line=" + line.No + " and numero='" + doc.No + "'";
     
    try
    {
    // SqlFactory est ma classe perso d'exécution de requêtes
    SqlFactory.ExecuteNonQuery(queryDelBLLine);
    SqlFactory.ExecuteNonQuery(queryAddFALine);
     } catch {  }
     
            countLines++;
    }
    Je précise qu'il n'y a pas de binding direct entre mes lignes de bon de livraison supprimé et LstDocLigne (la liste est crée une fois et n'est jamais retouché quelque soit ce qu'il se passe réellement dans le BL de la base de données)

    Voilou voilou

  7. #7
    Membre émérite
    Profil pro
    Mangeur de gauffre
    Inscrit en
    Octobre 2007
    Messages
    4 413
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Mangeur de gauffre

    Informations forums :
    Inscription : Octobre 2007
    Messages : 4 413
    Points : 2 498
    Points
    2 498
    Par défaut
    Salut

    J'ai appris a me mefier des

    Citation Envoyé par thelpi
    ça ressemble fortement à ça
    9 fois sur dix quand on ne trouve pas un bug c'est precisément parce qu'on cherche la on on pense mais pas la ou il est auquel on n'avait pas pensé

    Sincerement j'ai des dizaines d'applications avec des listes sur lesquel je fais forcément des boucles (for next)
    Et si il y avait eu la moindre réalité d'un bug possible sur le count ca fait longtemps que je l'aurais personnemement vécu
    Et je pense ne pas etre le seul avec toi a utiliser le Count des List
    Meme google ne semble pas etre informé de ta découverte

    Vérifie donc bien tout ce que tu fait dans Addlog et dans l'environnement qui pourrait affecter
    Et sincerement essaye essaye le for next, eu foreach avec itérateur c'est vraiment bancal !

    Quite a vérifier a chaque itération si le count courrant est le meme que celui que tu avait avant le début de ta boucle

  8. #8
    Membre habitué Avatar de thelpi
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    217
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Deux Sèvres (Poitou Charente)

    Informations forums :
    Inscription : Décembre 2007
    Messages : 217
    Points : 156
    Points
    156
    Par défaut
    Pour info, le code de la fonction AddLog :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    private void AddLog(string log)
    {
         // AddLog est appelé depuis un BackgroundWorker
         this.Dispatcher.Invoke(new StringHandler(delegate(string _log) { this.ListBoxDeLogs.Add(_log); }), new object[] { log });
    }
     
    private delegate void StringHandler(string msg);
    Sinon, je pense que je vais utiliser le for next, comme recommandé, mais bon si le bug revient tout les 2 mois je suis pas près de mettre le topic résolu...

    Par ailleurs, en voyant le code que j'ai écrit, je pense à une question qui me trotte depuis longtemps :
    ce genre de délégués (StringHandler), assez banal, y'aurait il un endroit (dans la msdn par exemple) proposant une liste de délégué déjà existant dans le framework ( pr éviter une ligne de code en plus )

    J'ai le souvenir d'avoir utilisé mon propre délégué void sans paramètres pendant un an sans savoir que "Action" existait...

    Merci à vous en tout cas !

  9. #9
    Membre émérite
    Profil pro
    Mangeur de gauffre
    Inscrit en
    Octobre 2007
    Messages
    4 413
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Mangeur de gauffre

    Informations forums :
    Inscription : Octobre 2007
    Messages : 4 413
    Points : 2 498
    Points
    2 498
    Par défaut
    Tiens tiens !

    Tu travaille avec plusieurs thread ?

    C'est lequel qui pourrait chatouiller doc.LstDocLigne pendant que tu le boucle

  10. #10
    Membre habitué Avatar de thelpi
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    217
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Deux Sèvres (Poitou Charente)

    Informations forums :
    Inscription : Décembre 2007
    Messages : 217
    Points : 156
    Points
    156
    Par défaut
    Alors, pour aller plus loin :
    j'ai 2 thread, ma Form Wpf et un BackgroundWorker.

    La form ne fait rien une fois le bgw lancé (excepté donc l'affichage des logs), j'ai mis ça en place uniquement pr éviter de bloquer la forme pendant le traitement.

    Par contre, ma fenêtre est appelée depuis une autre par le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    globalTimer.Stop();
    new wdwInvoicing(docToEncaiss).ShowDialog();
    globalTimer.Start();
    Je pense que c'est là ou ça devient intéressant

    Comme dit plus haut, mon objet doc et sa collection de lignes ne sont reliés qu'une fois, à l'initialisation, au contenu réel de la base de données.

    Par contre, le globalTimer permet justement de rafraichir à intervalles réguliers l'objet doc dans le cas ou ça bougerait dans la BD.

    On peut railler autant qu'on veut cette façon de faire ^^, mais ça me paraissait clean tant que j'arrêtais le Timer avant la gestion de mon document...

    Maintenant je pense plus à un problème de Timer pas arrêté qu'à un pb de count ( effectivement, si le Timer n'est pas arrêtée, il rafraichira le nb de lignes du document pendant le traitement... ) !

    Dernier détail : la seule fois ou est appelée la fenêtre est dans le bout de code (je ne l'appelle jamais sans avoir oublié le

  11. #11
    Membre émérite
    Profil pro
    Mangeur de gauffre
    Inscrit en
    Octobre 2007
    Messages
    4 413
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Mangeur de gauffre

    Informations forums :
    Inscription : Octobre 2007
    Messages : 4 413
    Points : 2 498
    Points
    2 498
    Par défaut
    Ca commence a se dégager on dirait !

  12. #12
    Membre actif
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    203
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 203
    Points : 220
    Points
    220
    Par défaut
    Par ailleurs, en voyant le code que j'ai écrit, je pense à une question qui me trotte depuis longtemps :
    ce genre de délégués (StringHandler), assez banal, y'aurait il un endroit (dans la msdn par exemple) proposant une liste de délégué déjà existant dans le framework ( pr éviter une ligne de code en plus )
    Si il y a la série des void Action<T>(T obj) et des TResult Func<T, TResult>(T arg) dans ton cas tu peux écrire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
     this.Dispatcher.Invoke(new Action<string>( _log => this.ListBoxDeLogs.Add(_log)), new object[] { log });

  13. #13
    Membre habitué Avatar de thelpi
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    217
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Deux Sèvres (Poitou Charente)

    Informations forums :
    Inscription : Décembre 2007
    Messages : 217
    Points : 156
    Points
    156
    Par défaut
    Merci matdur pour l'info, je connaissais pas ce système et ça à l'air bien dans l'esprit de ce que je cherche.

    Olibara, une idée alors pour le timer.Stop() ?

  14. #14
    Membre émérite
    Profil pro
    Mangeur de gauffre
    Inscrit en
    Octobre 2007
    Messages
    4 413
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Mangeur de gauffre

    Informations forums :
    Inscription : Octobre 2007
    Messages : 4 413
    Points : 2 498
    Points
    2 498
    Par défaut
    Citation Envoyé par thelpi
    Olibara, une idée alors pour le timer.Stop() ?
    Dificille a dire sans avoir tout le code sous les yeux et ma boule de cristal est un peu au repos

    Mais il est clair que le problème est lié a un refresh de ta liste d'une maniere ou d'une autre

  15. #15
    Membre habitué Avatar de thelpi
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    217
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Deux Sèvres (Poitou Charente)

    Informations forums :
    Inscription : Décembre 2007
    Messages : 217
    Points : 156
    Points
    156
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    /* L'événement Elapsed est déclenché sur un thread ThreadPool, pour que la méthode de gestion d'événements puisse s'exécuter sur un thread pendant qu'un appel à la méthode Stop s'exécute sur un autre thread. Cela peut provoquer le déclenchement de l'événement Elapsed après l'appel de la méthode Stop. */
    On se rapproche je crois

    Par contre, le code qu'ils utilisent pour empêcher le problème me semble assez costaud...

  16. #16
    Membre actif
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    203
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 203
    Points : 220
    Points
    220
    Par défaut
    Si tu ne veux pas que la liste soit rafraichit pendant l'itération tu as deux solutions
    - soit tu fais une copie de la liste et tu itères sur la copie
    - soit tu ajoutes des lock pour protéger l'accès à la liste

  17. #17
    Membre émérite
    Profil pro
    Mangeur de gauffre
    Inscrit en
    Octobre 2007
    Messages
    4 413
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Mangeur de gauffre

    Informations forums :
    Inscription : Octobre 2007
    Messages : 4 413
    Points : 2 498
    Points
    2 498
    Par défaut
    Ou plus simple

    Tu derive ta list dans une classe ou tu ajoute un booleen genre
    UpdateInProcess et avec ca tu dis pas touche a ton event de refresh !!

  18. #18
    Membre habitué Avatar de thelpi
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    217
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Deux Sèvres (Poitou Charente)

    Informations forums :
    Inscription : Décembre 2007
    Messages : 217
    Points : 156
    Points
    156
    Par défaut
    Ok ok, je vois le truc, je vais essayer d'implémenter soit la copie, soit le booléen.

    S'il s'avère que j'ai finalement toujours mon pb j'upperai le topic, pour le moment je vais mettre

    Merci à vous 2 pour votre patience et vos conseils

    Bonne soirée.

  19. #19
    Membre actif
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    203
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 203
    Points : 220
    Points
    220
    Par défaut
    Tu derive ta list dans une classe ou tu ajoute un booleen genre
    UpdateInProcess et avec ca tu dis pas touche a ton event de refresh !!
    Non, le coup du boolean n'est pas thread safe
    Si tu veux une implémentation thread safe, il faut que la collection soit thread safe. Et après si tu veux itérer la collection il faut la mettre entre un lock

  20. #20
    Membre émérite
    Profil pro
    Mangeur de gauffre
    Inscrit en
    Octobre 2007
    Messages
    4 413
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Mangeur de gauffre

    Informations forums :
    Inscription : Octobre 2007
    Messages : 4 413
    Points : 2 498
    Points
    2 498
    Par défaut
    Citation Envoyé par matdur
    Non, le coup du boolean n'est pas thread safe
    Tu a raison
    Dans le cas présent, a mon humble avis, je pensais que le booleen etait sufisant car il s'agit de se proteger d'un event qui se produit fortuitement alors qu'il n'est pas cencé se produire

    Mais je pense qu'il faut aussi proteger dans l'autre sens

    Eviter de commencer a traiter la liste alors que la DB la rafraichirt encore

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Bug API Poi connu ?
    Par jadey dans le forum Documents
    Réponses: 4
    Dernier message: 29/05/2009, 15h00
  2. liste deroulante et touche entree bug
    Par lolothom dans le forum IHM
    Réponses: 10
    Dernier message: 09/09/2007, 08h33
  3. [BDE] Liste des bugs
    Par bezot3 dans le forum C++Builder
    Réponses: 2
    Dernier message: 05/01/2005, 18h24
  4. Compter le nombre ligne listée (COUNT) ?
    Par StouffR dans le forum Langage SQL
    Réponses: 7
    Dernier message: 02/09/2002, 10h41

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