2012-12-22 1 views
40

Nel seguente pezzo di codice,Come creare una nuova copia profonda (clone) di un elenco <T>?

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; 
     } 
    } 

} 

Io uso un tipo Book oggetto per creare un List<T> e popolarlo con alcuni elementi dando loro un titolo unico (da 'uno' a 'cinque').

Quindi creo List<Book> books_2 = new List<Book>(books_1).

Da questo punto, so che è un clone dell'oggetto elenco, MA gli oggetti libro da book_2 sono ancora un riferimento dagli oggetti libro in books_1. È provato modificando i 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 possiamo creare una nuova copia cartacea di un List<T>? L'idea è che books_1 e books_2 diventano completamente indipendenti l'uno dall'altro.

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

Ciò che sarebbe davvero fantastico per gli helper è utilizzare il mio codice e modificarlo con una soluzione praticabile in modo che possa essere compilato e lavorato. Penso che aiuterà veramente i neofiti a cercare di capire le soluzioni offerte per questo problema.

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

+0

Qualcosa come "Copia"? –

+0

Questo tipo di copia viene normalmente chiamato [copia profonda] (http: //en.wikipedia.org/wiki/Deep_copy # Deep_copy). Se ritieni che "copia cartacea" sia qualcosa di diverso di cui hai effettivamente bisogno nel tuo caso, ti preghiamo di annullare la mia modifica e aggiungere la definizione del termine. –

+1

Una [copia cartacea] (http://en.wikipedia.org/wiki/Hard_copy) significa che lo si stampa su un foglio di carta fisico. –

risposta

80

è necessario creare nuovi oggetti Book poi mettere quelli in una nuova List:

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

Aggiornamento: Un po 'più semplice ... List<T> ha un metodo chiamato ConvertAll che restituisce una nuova lista:

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

E se l'oggetto Book fosse più complesso e avesse migliaia di altre proprietà? – TheScholar

+11

+1, @TheScholar - di quanto tu possa creare un copy contructor o implementare un altro modo per creare Deep-copy di un oggetto. –

+6

@TheScholar: Allora sarebbe una classe mal progettata. Migliaia di proprietà ... sei serio? –

18

Creare un'interfaccia generica ICloneable<T> da implementare nella classe Book in modo che la classe sappia come creare una copia di se stessa.

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

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

È quindi possibile utilizzare l'LINQ o ConvertAll metodi che Mark menzionato.

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

o

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

Fantastico! Grazie ! –

+0

nice solution man, questo mi ha salvato dal riscrivere tonnellate di nuovo codice. – Mana

16

Sono deluso Microsoft non ha offerto una soluzione pulita, veloce e facile come Ruby stanno facendo con il metodo clone().

Tranne che fa non creare una copia completa, si crea una copia.

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

  1. Ciclo nel grafico dell'oggetto. Ad esempio, Book ha un Author e Author ha un elenco dei suoi Book s.
  2. Riferimento ad un oggetto esterno. Ad esempio, un oggetto può contenere Stream aperto che scrive su un file.
  3. Eventi. Se un oggetto contiene un evento, praticamente chiunque potrebbe esserne iscritto. Questo può diventare particolarmente problematico se l'abbonato è qualcosa come una GUI Window.

ora, ci sono fondamentalmente due modi come clonare qualcosa:

  1. implementare un metodo Clone() in ogni classe che è necessario clonato. (C'è anche l'interfaccia ICloneable, ma dovresti usare lo non, usando l'interfaccia personalizzata ICloneable<T> come suggerito da Trevor.) Se sai che tutto ciò che serve è creare una copia superficiale di ogni campo di questa classe, potresti utilizzare MemberwiseClone() per implementarlo. In alternativa, è possibile creare un "copy constructor": public Book(Book original).
  2. Utilizzare la serializzazione per serializzare gli oggetti in un MemoryStream e quindi deserializzare di nuovo. Ciò richiede che contrassegni ogni classe come [Serializable] e si possa anche configurare cosa esattamente (e come) dovrebbe essere serializzato. Ma questa è più una soluzione "rapida e sporca" e molto probabilmente anche meno performante.
1

Dal Clone sarebbe restituire un'istanza oggetto del Libro, quell'oggetto sarebbe necessario prima di essere gettato in un libro prima di poter chiamare ToList su di esso. L'esempio di cui sopra deve essere scritto come:

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

Se la classe Array soddisfa le proprie esigenze, si potrebbe anche utilizzare il metodo List.ToArray, che copia gli elementi per un nuovo array.

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

+2

nel mio caso questo era l'approccio più semplice. ad es .: var tagToRead = new List (tagsFailed.ToArray()); –

+3

Tranne che gli oggetti nella matrice sono ancora riferimenti agli elementi nell'elenco originale. –

3

Beh,

Se si contrassegna tutte le classi coinvolte come serializzabile è possibile:

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);  
} 

Fonte:

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

-2

o si può solo semplicemente addrange a boo k_2:

book_2.AddRange(book_1); 

Bang! Viene creata una copia profonda !!!

Problemi correlati