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.

8voto

Virepri Points 119
List<Book> books_2 = new List<Book>(books_2.ToArray());

Cela devrait faire exactement ce que vous voulez. Démonstration ici.

5voto

Hossein Ebrahimi Points 109

C# 9 dossiers y avec des expressions peut rendre les choses un peu plus faciles, surtout si votre type a de nombreuses propriétés.

Vous pouvez utiliser quelque chose comme :

var books2 = books1.Select(b => b with { }).ToList();

Je l'ai fait en tant que exemple :

record Book
{
    public string Name { get; set; }
}

static void Main()
{
    List<Book> books1 = new List<Book>()
    {
        new Book { Name = "Book1.1" },
        new Book { Name = "Book1.2" },
        new Book { Name = "Book1.3" }
    };

    var books2 = books1.Select(b => b with { }).ToList();

    books2[0].Name = "Changed";
    books2[1].Name = "Changed";

    Console.WriteLine("Books1 contains:");
    foreach (var item in books1)
    {
        Console.WriteLine(item);
    }

    Console.WriteLine("Books2 contains:");
    foreach (var item in books2)
    {
        Console.WriteLine(item);
    }
}

Le résultat est le suivant : (les modifications apportées aux objets de Books2 n'ont pas affecté les objets originaux de Books1).

Books1 contient :

Livre { Nom = Livre1.1 }

Livre { Nom = Livre1.2 }

Livre { Nom = Livre1.3 }

Books2 contient :

Livre { Name = Changed }

Livre { Name = Changed }

Livre { Nom = Livre1.3 }

2voto

Nate Points 537

Depuis Clone renverrait un objet instance de Book, cet objet devrait d'abord être transformé en Book avant que vous puissiez appeler ToList sur elle. L'exemple ci-dessus doit être écrit comme suit :

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

2voto

user3192640 Points 31
public static class Cloner
{
    public static T Clone<T>(this T item)
    {
        FieldInfo[] fis = item.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
        object tempMyClass = Activator.CreateInstance(item.GetType());
        foreach (FieldInfo fi in fis)
        {
            if (fi.FieldType.Namespace != item.GetType().Namespace)
                fi.SetValue(tempMyClass, fi.GetValue(item));
            else
            {
                object obj = fi.GetValue(item);
                if (obj != null)
                    fi.SetValue(tempMyClass, obj.Clone());
            }
        }
        return (T)tempMyClass;
    }
}

0voto

JHaps Points 47

Si la classe Array répond à vos besoins, vous pouvez également utiliser la méthode List.ToArray, qui copie les éléments dans un nouveau tableau.

Référence : http://msdn.microsoft.com/en-us/library/x303t819(v=vs.110).aspx

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