it-swarm.com.de

Wie erstellt man eine neue tiefe Kopie (Klon) einer Liste <T>?

Im folgenden Stück Code, 

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 verwende einen Book-Objekttyp, um einen List<T> zu erstellen, und fülle ihn mit einigen Elementen auf, die ihm einen eindeutigen Titel geben (von 'eins' bis 'fünf').

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

Von diesem Punkt an weiß ich, dass es ein Klon des Listenobjekts ist, ABER die Buchobjekte von book_2 sind immer noch eine Referenz aus den Buchobjekten in books_1. Es hat sich bewährt, indem Sie Änderungen an den beiden ersten Elementen von books_2 vornehmen und dann dieselben Elemente von book_1 in einer TextBox überprüfen.

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

JETZT DIE FRAGE

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

Ich bin enttäuscht, dass Microsoft keine ordentliche, schnelle und einfache Lösung angeboten hat, wie sie Ruby mit der clone()-Methode macht.

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

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

53
TheScholar

Sie müssen neue Book-Objekte erstellen und diese dann in eine neue List einfügen:

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

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

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

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

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 von Mark erwähnten linq- oder ConvertAll-Methoden verwenden.

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

oder

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

Ich bin enttäuscht, dass Microsoft keine ordentliche, schnelle und einfache Lösung angeboten hat, wie sie Ruby mit der clone()-Methode macht.

Mit der Ausnahme, dass not keine tiefe Kopie erstellt, wird eine flache Kopie erstellt.

Beim tiefen Kopieren müssen Sie immer vorsichtig sein, was genau Sie kopieren möchten. Beispiele für mögliche Probleme sind:

  1. Zyklus in der Objektgrafik. Beispielsweise hat Book eine Author und Author eine Liste seiner Books.
  2. Verweis auf ein externes Objekt. Ein Objekt könnte beispielsweise eine Stream-Datei enthalten, die in eine Datei schreibt.
  3. Veranstaltungen. Wenn ein Objekt ein Ereignis enthält, kann es so ziemlich jeder abonnieren. Dies kann besonders problematisch werden, wenn es sich bei dem Abonnenten um eine GUI Window handelt.

Nun gibt es zwei Möglichkeiten, etwas zu klonen:

  1. Implementieren Sie eine Clone()-Methode in jeder Klasse, die Sie klonen müssen. (Es gibt auch eine ICloneable-Schnittstelle, aber Sie sollten not nicht verwenden; die Verwendung einer benutzerdefinierten ICloneable<T>-Schnittstelle, wie von Trevor vorgeschlagen, ist in Ordnung.) Sie könnten MemberwiseClone() verwenden, um es zu implementieren. Alternativ können Sie einen "Kopierkonstruktor" erstellen: public Book(Book original).
  2. Verwenden Sie die Serialisierung, um Ihre Objekte in eine MemoryStream zu serialisieren und anschließend wieder zu deserialisieren. Dazu müssen Sie jede Klasse als [Serializable] kennzeichnen und es kann auch konfiguriert werden, was genau (und wie) serialisiert werden soll. Dies ist jedoch eher eine "schnelle und schmutzige" Lösung und wird höchstwahrscheinlich auch weniger leistungsfähig sein.
17
svick
List<Book> books_2 = new List<Book>(books_2.ToArray());

Das sollte genau das tun, was Sie wollen. Hier demonstriert.

7
Virepri

Gut,

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

6
gatsby

Da Clone eine Objektinstanz von Book zurückgeben würde, müsste dieses Objekt zunächst in ein Book umgewandelt werden, bevor Sie ToList dort aufrufen können. Das obige Beispiel muss wie folgt geschrieben werden:

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

Wenn die Array-Klasse Ihren Anforderungen entspricht, können Sie auch die List.ToArray-Methode verwenden, mit der Elemente in ein neues Array kopiert werden. 

Referenz: http://msdn.Microsoft.com/de-de/library/x303t819(v=vs.110).aspx

2
JHaps

Sie können dies verwenden:

var newList= JsonConvert.DeserializeObject<List<Book>>(list.toJson());
1
Thao Le
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;
    }
}
1
user3192640