none
Listes en c#

    Question

  • Bonjour,

     

    je débute en C sharp et j'ai quelques problèmes pour utiliser les listes.
    Je souhaite utiliser les fonctions Contains, Remove et le constructeur pour copier une liste.

    Pour les listes d'éléments simples (int,...) , je n'ai pas de problèmes. Mais je me suis créé une liste d'une classe que j'ai implémentée, et les fonctions précédemment citées ne marchent plus.
    J'ai l'impression que pour les éléments plus complexes, on ne compare pas les éléments mais leurs adresses.

    Par exemple, la copie (list <une_classe> nouvelle_liste = new list<une_classe> (ancienne_liste)) ne fait pas une copie, il s'agit de la même liste et une suppression d'un élément de l'ancienne liste affecte la nouvelle.

    J'ai essayé de réécrire la fonction Equals pour les problèmes de Contains ou Remove, mais ça n'a pas marché.

     

    Merci.

    Sunday, July 11, 2010 1:07 PM

Answers

  • Bonjour,

    Pour un élément complexe (par exemple un utilisateur avec un nom et un prénom), la comparaison est à définir explicitement. D'après http://msdn.microsoft.com/fr-fr/library/6sh2ey19.aspx :

    "Les méthodes telles que Contains, IndexOf, LastIndexOf et Remove utilisent un comparateur d'égalité pour les éléments de liste.Le comparateur d'égalité par défaut pour le type T est déterminé comme suit.Si le type T implémente l'interface générique IEquatable<T>, le comparateur d'égalité est la méthode Equals(T) de cette interface ; sinon, le comparateur d'égalité par défaut est Object.Equals(Object)."

    Par contre pour le new, cela devrait bien créer une liste différente. Cela me donne par exemple :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication1
    {
     class Program
     {
    
     class User : IEquatable<User>
     {
      public string FirstName { get; set; }
      public string LastName { get; set; }
    
      public bool Equals(User other)
      {
      return (this.LastName == other.LastName) && (this.FirstName == other.FirstName);
      }
     }
    
     static void Main(string[] args)
     {
      User u;
      List<User> l1;
      List<User> l2;
      l1 = new List<User> { new User { LastName = "Hugo", FirstName = "Victor" }, new User { LastName = "Hugo", FirstName = "Marcel" } };
      u = new User { LastName = "Hugo", FirstName = "Marcel" };
      Console.WriteLine(l1.Contains(u));
      l2 = new List<User>(l1);
      Console.WriteLine(l1.Count + "/" + l2.Count);
      l1.Remove(u);
      Console.WriteLine(l1.Count + "/" + l2.Count);
      Console.WriteLine("Pressez une touche pour continuer...");
      Console.ReadKey();
     }
     }
    }
    

    ce qui me donne bien :

    true (l'élement est contenu dans la liste)
    2/2 les deux listes ont le même nombre d'élements
    1/2 la liste 1 ne contient plus qu'un élement mais la liste 2 a toujours 2 éléments...

    Si le problème sur les listes persiste le plus simple est de montrer le code minimal qui montre le problème...  Par contre, les objets sont copiés (un objet n'est qu'un pointeur) donc les objets présents dans la deuxième liste sont les mêmes que ceux présents dans la première même si les listes elle-mêmes ne sont pas identiques. Serait-ce le problème ?

     


    Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".
    Sunday, July 11, 2010 4:36 PM
  • SAlut Jenny00,

    En relisant ta question, je comprend que tu veux copier la liste.  Ton exemple:

    list <une_classe> nouvelle_liste = new list<une_classe> (ancienne_liste)) 
    

    Ici tu crée une nouvelle liste contenant les éléments de l'ancienne liste. Les éléments ne sont pas copiés, la nouvelle liste contient les éléments. De ce fait, plusieurs liste peuvent contenir les mêmes éléments sans problème.

    Quand tu fais le Contains, il faut déjà connaitre l'objet que l'on cherche et le passé en paramètre, ce qui n'est pas pratique si on ne connait qu'une partie de l'objet (par exemple son ID).

    Voici un exemple avec Linq qui regarde pour l'ID 23:

    public partial class Form1 : Form
      {
        public Form1()
        {
          InitializeComponent();
    
          List<UneClasse> maListe = new List<UneClasse>();
    
          maListe.Add(new UneClasse(12));
          maListe.Add(new UneClasse(67));
          maListe.Add(new UneClasse(23));
          maListe.Add(new UneClasse(12));
          maListe.Add(new UneClasse(15));
    
          UneClasse LaClasse= maListe.Where(p => p.ID == 23).FirstOrDefault(); 
          
    
        }
      }
      public class UneClasse
      {
        public UneClasse(int Id)
        {
          ID = Id;
        }
        public int ID { get; set; }
      }
    

     


    Microsoft MVP C# || gabrielmongeon.com
    Sunday, July 11, 2010 5:03 PM

All replies

  • Quelle version du framework ou Visual Studio utilises-tu? Tu peux toujours utilisé Linq to Objects, cela va sûrement changer ton approche.
    Microsoft MVP C# || gabrielmongeon.com
    Sunday, July 11, 2010 3:30 PM
  • Une version récente. Mais je débute tout juste et je ne connais pas encore Linq. C'est la seule solution a mon problème ?
    Sunday, July 11, 2010 3:54 PM
  • Bonjour,

    Pour un élément complexe (par exemple un utilisateur avec un nom et un prénom), la comparaison est à définir explicitement. D'après http://msdn.microsoft.com/fr-fr/library/6sh2ey19.aspx :

    "Les méthodes telles que Contains, IndexOf, LastIndexOf et Remove utilisent un comparateur d'égalité pour les éléments de liste.Le comparateur d'égalité par défaut pour le type T est déterminé comme suit.Si le type T implémente l'interface générique IEquatable<T>, le comparateur d'égalité est la méthode Equals(T) de cette interface ; sinon, le comparateur d'égalité par défaut est Object.Equals(Object)."

    Par contre pour le new, cela devrait bien créer une liste différente. Cela me donne par exemple :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication1
    {
     class Program
     {
    
     class User : IEquatable<User>
     {
      public string FirstName { get; set; }
      public string LastName { get; set; }
    
      public bool Equals(User other)
      {
      return (this.LastName == other.LastName) && (this.FirstName == other.FirstName);
      }
     }
    
     static void Main(string[] args)
     {
      User u;
      List<User> l1;
      List<User> l2;
      l1 = new List<User> { new User { LastName = "Hugo", FirstName = "Victor" }, new User { LastName = "Hugo", FirstName = "Marcel" } };
      u = new User { LastName = "Hugo", FirstName = "Marcel" };
      Console.WriteLine(l1.Contains(u));
      l2 = new List<User>(l1);
      Console.WriteLine(l1.Count + "/" + l2.Count);
      l1.Remove(u);
      Console.WriteLine(l1.Count + "/" + l2.Count);
      Console.WriteLine("Pressez une touche pour continuer...");
      Console.ReadKey();
     }
     }
    }
    

    ce qui me donne bien :

    true (l'élement est contenu dans la liste)
    2/2 les deux listes ont le même nombre d'élements
    1/2 la liste 1 ne contient plus qu'un élement mais la liste 2 a toujours 2 éléments...

    Si le problème sur les listes persiste le plus simple est de montrer le code minimal qui montre le problème...  Par contre, les objets sont copiés (un objet n'est qu'un pointeur) donc les objets présents dans la deuxième liste sont les mêmes que ceux présents dans la première même si les listes elle-mêmes ne sont pas identiques. Serait-ce le problème ?

     


    Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".
    Sunday, July 11, 2010 4:36 PM
  • SAlut Jenny00,

    En relisant ta question, je comprend que tu veux copier la liste.  Ton exemple:

    list <une_classe> nouvelle_liste = new list<une_classe> (ancienne_liste)) 
    

    Ici tu crée une nouvelle liste contenant les éléments de l'ancienne liste. Les éléments ne sont pas copiés, la nouvelle liste contient les éléments. De ce fait, plusieurs liste peuvent contenir les mêmes éléments sans problème.

    Quand tu fais le Contains, il faut déjà connaitre l'objet que l'on cherche et le passé en paramètre, ce qui n'est pas pratique si on ne connait qu'une partie de l'objet (par exemple son ID).

    Voici un exemple avec Linq qui regarde pour l'ID 23:

    public partial class Form1 : Form
      {
        public Form1()
        {
          InitializeComponent();
    
          List<UneClasse> maListe = new List<UneClasse>();
    
          maListe.Add(new UneClasse(12));
          maListe.Add(new UneClasse(67));
          maListe.Add(new UneClasse(23));
          maListe.Add(new UneClasse(12));
          maListe.Add(new UneClasse(15));
    
          UneClasse LaClasse= maListe.Where(p => p.ID == 23).FirstOrDefault(); 
          
    
        }
      }
      public class UneClasse
      {
        public UneClasse(int Id)
        {
          ID = Id;
        }
        public int ID { get; set; }
      }
    

     


    Microsoft MVP C# || gabrielmongeon.com
    Sunday, July 11, 2010 5:03 PM
  • Bonjour,

     

    j'ai l'impression que ce que je considérais comme étant un même problème (les problèmes de copie et ceux des méthodes sur les listes) sont en fait distincts d'après vos remarques.
    En ce qui concerne la copie de liste, ce que je veux c'est une copie des éléments également, car sinon je crois que quand je supprime un élément de la nouvelle liste, il est supprimé de l'ancienne (automatiquement, à cause du garbage collector non?).

    Comment faire une telle copie ?

    En ce qui concerne les méthodes, j'étais donc sur la bonne piste et je dois réimplémenter Equals. C'est bizarre car je croyais l'avoir fait, et ça avait échoué. Je vais réessayer. Quand j'implémente Equals, on me demande aussi d'implémenter GetHashCode. Ma classe comporte 3 attributs entiers, j'ai juste mis x^y^z. C'est ça que je dois mettre ?

     

    Merci.

    Sunday, July 11, 2010 11:35 PM
  • Salut,

    effectivement, je crois que tu avais 2 questioms imbriqués en une. Je vais essayer de répondre à votre question de copie:

    Il faut que tu vois une liste comme un contenant, ce contenant peut contenir n'importe quel objet(ici de type une_classe). En fait, tu peux même ajouté 2 fois le même objet dans la liste. L'objet tant qu'à lui peut être contenu dans plusieurs listes. 

    Lorsque tu fais Remove, tu enlève l'objet de la liste, contrairement à faire un Delete(supprimer, mais cela n'existe pas).  
    Lorsque tu fais Contains, tu dois lui donner une référence sur un objet de type une_classe et vérifie s'il a la même référence. Si jamais tu fais une copie et que tu fais un Contains avec celui pour retrouver l'original, celui-ci ne fonctionnera pas, car en mémoire ce sont maintenant 2 objets complètement distinct et unique. Je n'ai jamais travailler avec des copies, mis à part lorsque je voulais créer un objet à partir d'un autre.

    Donc, si l'objet est toujours contenu dans au moins une liste ou est stocké dans une variable, le Garbage Collector (GC) ne le recyclera pas. Mais si le GC ne voit aucune référence vers cette objet, donc ne peut pas être accèdé, alors le GC le recycle.

    J'espère t'avoir éclairé un peu plus.


    Microsoft MVP C# || gabrielmongeon.com
    Monday, July 12, 2010 12:50 AM
  • Bonjour,

    Le code que j'ai posté montre que supprimer un élément d'une liste ne le supprime pas de l'autre. Je pense qu'il est possible que tu interprètres mal le problème que tu vois (peut-être le fait que les objets eux sont identiques ce qui est classique avec les objets ?)

    Idem pour GetHashCode, il faudrait voir ce que tu essayes d'implémenter comme interface.

    Il serait plus simple de partir d'un code concret, réduit au mininum nécessaire et suffisant pour montrer le problème, plutôt que d'une description forcément moins précise.

     


    Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".
    Monday, July 12, 2010 8:49 AM
  • Bonjour,

     

    Merci à tous pour la contribution dans ce thread. Jenny, je vous prie de confirmer si vous êtes encore intéressée dans ce problème ou si les explications présentées ici ont été satisfaisantes.

     

    Cordialement,

    Alex

    ________________

    Publiez un article sur une de ces technologies : Visual Basic, C#, C++, .NET, ASP.NET, SQL Server, Silverlight, SharePoint 2010, SharePoint 2007

    Astuces pour Visual Studio 2010

    Didacticiels et astuces : VB.NET, C#, ASP.NET, .NET Framework, Silverlight, Workflow Foundation, WPF

    Café des usages

    Microsoft propose ce service gratuitement, dans le but d'aider les utilisateurs et d'élargir les connaissances générales liées aux produits et technologies Microsoft. Ce contenu est fourni "tel quel" et il n'implique aucune responsabilité de la part de Microsoft.

     

     

     

    Thursday, July 15, 2010 1:17 PM
  • Bonjour,

     

    j'ai toujours quelques petits soucis.
    Je donne cette fois un exemple plus concret :

    J'ai une classe C.

    J'ajoute plusieurs éléments de C à une liste L.

    Je veux créer une matrice M où chaque case contient une copie (indépendante) de L.
    Voici comment je procède :

     

    List<C>[,] M = new List<C>[10, 10];
    for (int i = 0; i < 10; i++)
    {
     for (int j = 0; j<10; j++)
     {
      M[i, j] = new List<C>(L);
     }
    }
    

     

    Mais quand je supprime un élément de M, cela supprime un élément de L, ce que je ne souhaite pas.

    J'ai essayé de simplifier mon problème au maximum, j'espère que l'erreur est toujours présente.

    Merci.

     

    Saturday, July 17, 2010 11:24 AM
  • Bonjour,

    Merci de montrer un exemple complet y compris avec la déclaration de la liste L, la suppression dans M et l'examen à nouveau de la liste L.


    Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".
    Saturday, July 17, 2010 11:59 AM
  • Bonjour,

    étant donné que je ne suis pas sûr de réussir à refaire un exemple qui vous conviendra entièrement sans perdre d'informations, je vous propose de télécharger directement mon fichier .cs, qui est relativement petit.

    Vous le trouverez ici : http://www.2shared.com/file/sog5P4Fk/projet.html

    Mon problème est donc que quand on lance le programme en mode debug, avec la liste "pieces" dans l'espion et un point d'arrêt à la ligne 140, on s'aperçoit qu'au bout du 11ème accès à cette ligne, la liste commence à perdre des éléments alors qu'on ne supprime pas directement des éléments de cette liste, mais seulement d'une matrice qui a été initialisé avec des copies de cette liste.

     

    Merci.

    Saturday, July 17, 2010 12:26 PM
  • Je n'ai pas VS sous la main mais je vois une ligne :

    non_fait[i, j] = pieces;

    qui devrait donc sans doute être changée en :

    non_fait[i, j] = new List<Piece>(pieces);

    pour créer effectivement une nouvelle liste qui contient les mêmes éléments plutôt que de référencer la même liste.

    Il serait sans doute possible de réorganiser le code pour éviter cette erreur. Par exemple en exposant pieces comme une propriété qui retournerait une copie d'une liste _pieces interne. Cela permettrait de garantir que l'on ne peut accéder qu'à une copie de la liste mais jamais à la liste originale elle-même.

     


    Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".
    Saturday, July 17, 2010 1:56 PM
  • Ah oui merci je l'avais oublié celui là, effectivement l'erreur ne se produit plus à présent.
    J'ai toutefois une autre erreur que je me permets de poster à la suite car c'est en lien avec le programme donné précédemment :

    il me dit "Une exception non gérée du type 'System.StackOverflowException' s'est produite dans ConsoleApplication1.exe".

    C'est bizarre car il ne me semble pas que l'algorithme soit faux.

    Est-il possible de changer la taille de la pile, ou encore mieux de lui faire comprendre qu'il n'a pas besoin d'attendre les résultats précédents (récursivité terminale) ?

    Saturday, July 17, 2010 2:44 PM
  • D'après ma recherche c'est 1 Mo par défaut (256 Ko sous ASP.NET et 4 Mo sous x64) et cela ne peut être changé qu'en modifiant le fichier EXE généré. Techniquement tout appel effectué doit avoir un retour.

    Si le code opère dans des conditions bien déterminées, je commencerais par vérifier à quelle profondeur de récursivité on parvient pour voir si cela semble bien correspondre à mon algo. Le code semble ne mettre fin à la récursivité que si new_piece==null et case_courante==0 ce qui me fait dire que l'on explore systématiquement toutes les possibilités.

    Seulement après vérification et si le code est effectivement correct, pour une récursivité réellement "massive", ma préférence personnelle serait sans doute de gérer la récursivité moi-même en empilant explicitement les données dans une pile (Stack<T>) ce qui permet :
    - de s'affranchir des problèmes de limite de la "vraie" pile
    - d'effacer ce que l'on veut de la pile si besoin

    Selon ce que doit faire le code une autre approche serait peut-être également possible (par exemple à base d'arbres en explorant les meilleures solutions d'abord et en élaguant les moins intéressantes pour ne pas essayer de tout explorer de façon exhaustive. A quoi correspond cet algo ?).

    Bonne continuation.


    Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".
    Sunday, July 18, 2010 9:00 AM