without c# list clone deep-copy

c# - without - ¿Cómo crear una nueva copia profunda(clon) de una Lista<T>?



c# clone list without reference (7)

En el siguiente fragmento de código,

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

Utilizo un tipo de objeto Book para crear una List<T> y la rellene con algunos elementos, dándoles un título único (de ''uno'' a ''cinco'').

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

Desde este punto, sé que es un clon del objeto de la lista, PERO los objetos del libro de book_2 todavía son una referencia de los objetos del libro en books_1 . Se ha probado haciendo cambios en los dos primeros elementos de books_2 y luego verificando esos mismos elementos de book_1 en un TextBox .

books_1[0].title and books_2[1].title han sido cambiados a los nuevos valores de books_2[0].title and books_2[1].title .

AHORA LA PREGUNTA

¿Cómo creamos una nueva copia de una List<T> ? La idea es que books_1 y books_2 vuelvan completamente independientes entre sí.

Estoy decepcionado de que Microsoft no haya ofrecido una solución ordenada, rápida y fácil como la que Ruby está haciendo con el método clone() .

Lo que sería realmente increíble de los ayudantes es usar mi código y modificarlo con una solución viable para que pueda compilarse y funcionar. Creo que realmente ayudará a los novatos a tratar de comprender las soluciones ofrecidas para este problema.

EDITAR: Tenga en cuenta que la clase de Book podría ser más compleja y tener más propiedades. Traté de mantener las cosas simples.


Estoy decepcionado de que Microsoft no haya ofrecido una solución ordenada, rápida y fácil como la que Ruby está haciendo con el método clone() .

Excepto que no crea una copia profunda, crea una copia superficial.

Con la copia profunda, debe tener siempre cuidado, qué es exactamente lo que quiere copiar. Algunos ejemplos de posibles problemas son:

  1. Ciclo en el gráfico de objetos. Por ejemplo, Book tiene un Author y Author tiene una lista de sus Book s.
  2. Referencia a algún objeto externo. Por ejemplo, un objeto puede contener una Stream abierta que escribe en un archivo.
  3. Eventos. Si un objeto contiene un evento, prácticamente cualquiera podría suscribirse. Esto puede ser especialmente problemático si el suscriptor es algo así como una Window GUI.

Ahora, básicamente hay dos formas de clonar algo:

  1. Implemente un método Clone() en cada clase que necesite clonar. (También hay ICloneable interfaz ICloneable , pero no debe usar eso, ya que el uso de una interfaz ICloneable<T> personalizada como Trevor sugiere está bien). Si sabe que todo lo que necesita es crear una copia superficial de cada campo de esta clase, podría usar MemberwiseClone() para implementarlo. Como alternativa, puede crear un "constructor de copias": public Book(Book original) .
  2. Use la serialización para serializar sus objetos en un MemoryStream y luego deserializarlos. Esto requiere que marque cada clase como [Serializable] y también se puede configurar exactamente (y cómo) se debe serializar. Pero esto es más una solución "rápida y sucia", y probablemente también sea menos eficiente.


Como Clone devolvería una instancia de objeto de Libro, ese objeto primero tendría que ser ToList a un Libro antes de que pueda invocar a ToList en él. El ejemplo anterior debe escribirse como:

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


Cree una ICloneable<T> genérica ICloneable<T> que implemente en su clase Book para que la clase sepa cómo crear una copia de sí mismo.

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

A continuación, puede utilizar los métodos linq o ConvertAll mencionados por Mark.

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

o

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


Necesita crear nuevos objetos de Book luego ponerlos en una nueva List :

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

Actualización: Ligeramente más simple ... List<T> tiene un método llamado ConvertAll que devuelve una nueva lista:

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



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

Eso debería hacer exactamente lo que quieras. Demostrado aquí.