Come creare una nuova copia profonda (clone) di una lista ?

Nel seguente pezzo di codice,

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 books_1 = new List(); 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 books_2 = new List(books_1); books_2[0].title = "Five"; books_2[1].title = "Six"; textBox1.Text = books_1[0].title; textBox2.Text = books_1[1].title; } } } 

Uso un tipo di object Book per creare un List e lo popolo con alcuni elementi che danno loro un titolo univoco (da “uno” a “cinque”).

Quindi creo List books_2 = new List(books_1) .

Da questo punto, so che è un clone dell’object lista, MA gli oggetti libro di book_2 sono ancora un riferimento dagli oggetti libro in books_1 . È dimostrato apportando modifiche ai due primi elementi di books_2 e quindi controllando gli stessi elementi di book_1 in un TextBox .

books_1[0].title and books_2[1].title sono stati effettivamente modificati con i nuovi valori di books_2[0].title and books_2[1].title .

ORA LA DOMANDA

Come creiamo una nuova copia di una List ? L’idea è che books_1 e books_2 diventino completamente indipendenti l’uno dall’altro.

Sono deluso che Microsoft non abbia offerto una soluzione semplice, veloce e semplice come Ruby sta facendo con il metodo clone() .

Quello che sarebbe davvero fantastico da parte degli helper è usare il mio codice e modificarlo con una soluzione praticabile in modo che possa essere compilato e funzionato. Penso che aiuterà veramente i neofiti a cercare di capire le soluzioni offerte per questo problema.

EDIT: si noti che la class Book potrebbe essere più complessa e avere più proprietà. Ho cercato di mantenere le cose semplici.

È necessario creare nuovi oggetti Book quindi inserirli in una nuova List :

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

Aggiornamento: leggermente più semplice … List ha un metodo chiamato ConvertAll che restituisce una nuova lista:

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

Crea ICloneable generica che implementa nella tua class Book modo che la class sappia come creare una copia di se stessa.

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

È quindi ansible utilizzare i metodi linq o ConvertAll indicati da Mark.

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

o

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

Sono deluso che Microsoft non abbia offerto una soluzione semplice, veloce e semplice come Ruby sta facendo con il metodo clone() .

Tranne che non crea una copia profonda, crea una copia superficiale.

Con una copia profonda, devi stare sempre attento, cosa esattamente vuoi copiare. Alcuni esempi di possibili problemi sono:

  1. Ciclo nel grafico dell’object. Ad esempio, il Book ha un Author e un Author ha una lista dei suoi Book .
  2. Riferimento ad alcuni oggetti esterni. Ad esempio, un object potrebbe contenere Stream aperto che scrive su un file.
  3. Eventi. Se un object contiene un evento, praticamente chiunque potrebbe esserne iscritto. Questo può diventare particolarmente problematico se l’utente è qualcosa come una Window GUI.

Ora, ci sono fondamentalmente due modi su come clonare qualcosa:

  1. Implementa un metodo Clone() in ogni class che hai bisogno di clonare. (C’è anche ICloneable interfaccia ICloneable , ma non dovresti usarlo, usando l’ ICloneable personalizzata come suggerito da Trevor va bene.) Se sai che tutto ciò che serve è creare una copia superficiale di ogni campo di questa class, potrebbe usare MemberwiseClone() per implementarlo. In alternativa, è ansible creare un “copy constructor”: public Book(Book original) .
  2. Usa la serializzazione per serializzare i tuoi oggetti in un MemoryStream e poi deserializzare di nuovo. Ciò richiede di contrassegnare ogni class come [Serializable] e può anche essere configurato cosa esattamente (e come) dovrebbe essere serializzato. Ma questa è più una soluzione “rapida e sporca” e molto probabilmente anche meno performante.

Bene,

Se contrassegni tutte le classi coinvolte come serializzabili puoi:

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

Fonte:

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

 List books_2 = new List(books_2.ToArray()); 

Questo dovrebbe fare esattamente quello che vuoi. Dimostrato qui.

Poiché Clone restituirebbe un’istanza di object di Book, quell’object dovrebbe prima essere eseguito su un Book prima di poter chiamare ToList su di esso. L’esempio sopra deve essere scritto come:

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

Se la class Array soddisfa le tue esigenze, puoi anche utilizzare il metodo List.ToArray, che copia gli elementi in un nuovo array.

Riferimento: http://msdn.microsoft.com/en-us/library/x303t819(v=vs.110).aspx