85 votes

Comment créer une nouvelle copie profonde (clone) d'un List<T> ?

Dans le morceau de code suivant,

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace clone_test_01
{

    public partial class MainForm : Form
    {

        public class Book
        {
            public string title = "";

            public Book(string title)
            {
                this.title = title;
            }
        }

        public MainForm()
        {
            InitializeComponent();

            List<Book> books_1 = new List<Book>();
            books_1.Add(  new Book("One")  );
            books_1.Add(  new Book("Two")  );
            books_1.Add(  new Book("Three")  );
            books_1.Add(  new Book("Four")  );

            List<Book> books_2 = new List<Book>(books_1);

            books_2[0].title = "Five";
            books_2[1].title = "Six";

            textBox1.Text = books_1[0].title;
            textBox2.Text = books_1[1].title;
        }
    }

}

J'utilise un Book pour créer un objet List<T> et je l'alimente avec quelques éléments en leur donnant un titre unique (de "un" à "cinq").

Je crée ensuite List<Book> books_2 = new List<Book>(books_1) .

A partir de là, je sais qu'il s'agit d'un clone de l'objet liste, MAIS les objets livres de book_2 sont toujours une référence des objets du livre dans books_1 . Elle est prouvée en apportant des modifications aux deux premiers éléments de books_2 puis en vérifiant ces mêmes éléments de book_1 dans un TextBox .

books_1[0].title and books_2[1].title ont en effet été remplacées par les nouvelles valeurs de books_2[0].title and books_2[1].title .

MAINTENANT LA QUESTION

Comment créer une nouvelle copie papier d'une List<T> ? L'idée est que books_1 y books_2 deviennent complètement indépendantes les unes des autres.

Je suis déçu que Microsoft n'ait pas proposé une solution nette, rapide et facile, comme Ruby le fait avec l'application clone() método.

Ce qui serait vraiment génial de la part des aides, c'est d'utiliser mon code et de le modifier avec une solution viable pour qu'il puisse être compilé et fonctionner. Je pense que cela aiderait vraiment les débutants qui essaient de comprendre les solutions proposées pour ce problème.

EDIT : Notez que le Book pourrait être plus complexe et avoir plus de propriétés. J'ai essayé de garder les choses simples.

127voto

Mark Byers Points 318575

Vous devez créer de nouveaux Book puis les placer dans un nouveau List :

List<Book> books_2 = books_1.Select(book => new Book(book.title)).ToList();

Mise à jour : un peu plus simple... List<T> possède une méthode appelée ConvertAll qui renvoie une nouvelle liste :

List<Book> books_2 = books_1.ConvertAll(book => new Book(book.title));

44voto

Trevor Pilley Points 7304

Créer un générique ICloneable<T> que vous mettez en œuvre dans votre Book afin que la classe sache comment créer une copie d'elle-même.

public interface ICloneable<T>
{
    T Clone();
}

public class Book : ICloneable<Book>
{
    public Book Clone()
    {
        return new Book { /* set properties */ };
    }
}

Vous pouvez alors utiliser soit la méthode linq, soit la méthode ConvertAll que Mark a mentionnées.

List<Book> books_2 = books_1.Select(book => book.Clone()).ToList();

ou

List<Book> books_2 = books_1.ConvertAll(book => book.Clone());

21voto

svick Points 81772

Je suis déçu que Microsoft n'ait pas proposé une solution nette, rapide et facile, comme Ruby le fait avec l'application clone() método.

Sauf que cela n'est pas le cas pas créer une copie profonde, il crée une copie superficielle.

Dans le cas de la copie profonde, il faut toujours faire attention à ce que l'on veut copier exactement. Voici quelques exemples de problèmes possibles :

  1. Cycle dans le graphe des objets. Par exemple, Book dispose d'un Author y Author a une liste de ses Book s.
  2. Référence à un objet extérieur. Par exemple, un objet peut contenir des Stream qui écrit dans un fichier.
  3. Événements. Si un objet contient un événement, pratiquement tout le monde peut y être abonné. Cela peut s'avérer particulièrement problématique si l'abonné est quelque chose comme une interface graphique Window .

Il y a deux façons de cloner quelque chose :

  1. Mettre en œuvre un Clone() dans chaque classe que vous souhaitez cloner. (Il existe également des méthodes ICloneable l'interface, mais vous devez pas utiliser cela ; utiliser un ICloneable<T> comme l'a suggéré Trevor). Si vous savez que tout ce dont vous avez besoin est de créer une copie superficielle de chaque champ de cette classe, vous pouvez utiliser MemberwiseClone() pour la mettre en œuvre. Une autre solution consiste à créer un "constructeur de copie" : public Book(Book original) .
  2. Utilisez la sérialisation pour sérialiser vos objets dans un fichier MemoryStream puis les désérialiser à nouveau. Pour ce faire, vous devez marquer chaque classe comme [Serializable] et il est également possible de configurer ce qui doit être sérialisé exactement (et comment). Mais il s'agit plus d'une solution "rapide et sale", et elle sera probablement moins performante.

10voto

gatsby Points 320

Bien,

Si vous marquez toutes les classes concernées comme sérialisables, vous pouvez :

public static List<T> CloneList<T>(List<T> oldList)  
{  
BinaryFormatter formatter = new BinaryFormatter();  
MemoryStream stream = new MemoryStream();  
formatter.Serialize(stream, oldList);  
stream.Position = 0;  
return (List<T>)formatter.Deserialize(stream);      
} 

Source :

https://social.msdn.microsoft.com/Forums/en-US/5c9b4c31-850d-41c4-8ea3-fae734b348c4/copy-listsomeobject-to-clone-list?forum=csharpgeneral

9voto

Thao Le Points 91

Vous pouvez utiliser ceci :

var newList= JsonConvert.DeserializeObject<List<Book>>(list.toJson());

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X