2012-12-22 9 views
40

Im folgenden Stück Code,Wie erstellen Sie eine neue tiefe Kopie (Klon) einer Liste <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; 
     } 
    } 

} 

Ich benutze ein Book Objekttyp ein List<T> und ich füllen Sie es mit ein paar Elemente, um sie einen einzigartigen Titel (von ‚Eins‘ bis ‚fünf‘), was zu schaffen.

Dann erstelle ich List<Book> books_2 = new List<Book>(books_1).

Von diesem Punkt weiß ich, dass es ein Klon des Listenobjekts ist, ABER die Buchobjekte von book_2 sind immer noch eine Referenz von den Buchobjekten in books_1. Es wurde bewiesen, indem Änderungen an den zwei ersten Elementen von books_2 vorgenommen wurden und dann dieselben Elemente von book_1 in einer TextBox überprüft wurden.

books_1[0].title and books_2[1].title wurden tatsächlich auf die neuen Werte von books_2[0].title and books_2[1].title geändert.

nun die Frage

Wie schaffen wir eine neue Fest Kopie eines List<T>? Die Idee ist, dass books_1 und books_2 völlig unabhängig voneinander werden.

Ich bin enttäuscht, Microsoft bot keine ordentliche, schnelle und einfache Lösung wie Ruby mit der clone() Methode.

Was von den Helfern wirklich fantastisch ist, meinen Code zu verwenden und ihn mit einer praktikablen Lösung zu ändern, damit er kompiliert werden und arbeiten kann. Ich denke, es wird wirklich Neulingen helfen, die angebotenen Lösungen für dieses Problem zu verstehen.

BEARBEITEN: Beachten Sie, dass die Klasse Book komplexer sein und mehr Eigenschaften haben kann. Ich habe versucht, die Dinge einfach zu halten.

+0

So etwas wie 'CopyTo'? –

+0

Diese Art des Kopierens wird normalerweise [deep copy] (http: //en.wikipedia.org/wiki/Deep_copy # Deep_copy). Wenn Sie glauben, dass "Hardcopy" etwas anderes ist, das Sie in Ihrem Fall wirklich benötigen, bitte machen Sie meine Bearbeitung rückgängig und fügen Sie Ihre Definition des Begriffs hinzu. –

+1

A [Hardcopy] (http://en.wikipedia.org/wiki/Hard_copy) bedeutet, dass Sie es auf ein Blatt Papier drucken. –

Antwort

80

Sie müssen neue Book Objekte erstellen dann die in einem neuen List setzen:

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

Update: Etwas einfacher ... List<T> eine Methode hat ConvertAll genannt, die eine neue Liste zurück:

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

Was wäre, wenn das Buchobjekt komplexer wäre und tausende andere Eigenschaften hätte? – TheScholar

+11

+1, @TheScholar - dann erstellen Sie entweder einen Kopierkonstruktor oder implementieren eine andere Möglichkeit, eine Deep-Kopie eines Objekts zu erstellen. –

+6

@TheScholar: Dann wäre es eine schlecht gestaltete Klasse. Tausende von Immobilien ... meinst du das ernst? –

18

Erstellen Sie eine generische ICloneable<T> Schnittstelle, die Sie in Ihrer Klasse Book implementieren, damit die Klasse weiß, wie sie eine Kopie von sich selbst erstellt.

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

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

Sie können dann entweder die Linq oder ConvertAll Methoden verwenden, die Mark erwähnt.

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

oder

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

Cool! Vielen Dank ! –

+0

nice solution man, das hat mich davor bewahrt, viel neuen Code zu schreiben. – Mana

16

Ich bin enttäuscht Microsoft nicht eine saubere, schnelle und einfache Lösung, wie Ruby bieten tun mit dem clone() Verfahren.

Außer, dass tut nicht eine tiefe Kopie erstellen, erstellt es eine flache Kopie.

Mit tiefem Kopieren müssen Sie immer vorsichtig sein, was genau Sie kopieren möchten. Einige Beispiele möglicher Probleme sind:

  1. Zyklus im Objektdiagramm. Zum Beispiel hat Book eine Author und Author hat eine Liste seiner Book s.
  2. Verweis auf ein externes Objekt. Ein Objekt könnte beispielsweise open Stream enthalten, das in eine Datei schreibt.
  3. Ereignisse. Wenn ein Objekt ein Ereignis enthält, kann es so gut wie jeder abonniert werden. Dies kann besonders problematisch werden, wenn es sich bei dem Abonnenten um eine GUI wie beispielsweise die Window handelt.

Nun gibt es grundsätzlich zwei Möglichkeiten, wie etwas zu klonen:

  1. Implementieren einer Clone() Methode in jeder Klasse, die Sie geklont benötigen. (Es gibt auch ICloneable Schnittstelle, aber Sie sollten nicht verwenden Sie, dass eine benutzerdefinierte ICloneable<T> Schnittstelle als Trevor vorgeschlagen ist in Ordnung.) Wenn Sie wissen, dass alles, was Sie brauchen, ist eine flache Kopie von jedem Feld dieser Klasse zu erstellen, könnten Sie Verwenden Sie MemberwiseClone(), um es zu implementieren. Als Alternative könnten Sie einen "Kopierkonstruktor" erstellen: public Book(Book original).
  2. Verwenden Sie die Serialisierung, um Ihre Objekte in eine MemoryStream serialisieren und sie dann wieder deserialisieren. Dazu müssen Sie jede Klasse als [Serializable] markieren und es kann auch konfiguriert werden, was genau (und wie) serialisiert werden soll. Aber das ist eher eine "schnelle und schmutzige" Lösung und wird wahrscheinlich auch weniger performant sein.
1

Da Clone eine Objektinstanz des Buches zurückkommen würde, würde das Objekt zuerst zu einem Buch werfen müssen, bevor Sie ToList auf sie anrufen können. Das obige Beispiel Bedürfnisse geschrieben werden als:

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

Wenn die Array-Klasse Ihren Bedürfnissen entspricht, können Sie auch die List.ToArray Methode verwenden könnte, die Kopien Elemente in ein neues Array.

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

+2

In meinem Fall war dies der einfachste Ansatz. zB: var tagsToRead = new Liste (tagsFailed.ToArray()); –

+3

Mit Ausnahme der Objekte im Array sind immer noch Verweise auf die Elemente in der ursprünglichen Liste. –

3

Nun,

Wenn Sie alle beteiligten Klassen als serialisierbar markieren können Sie:

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

Quelle:

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

-2

Oder könnten Sie einfach einfach addrange zu boo k_2:

book_2.AddRange(book_1); 

Bang! Eine tiefe Kopie wird erstellt !!!