Como criar uma nova cópia profunda (clone) de uma List ?

No seguinte trecho 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 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; } } } 

Eu uso um tipo de object Book para criar uma List e eu a preencho com alguns itens dando-lhes um título único (de ‘one’ a ‘five’).

Então eu crio List books_2 = new List(books_1) .

A partir desse ponto, sei que é um clone do object de lista, MAS os objects de livro do book_2 ainda são uma referência dos objects do livro em books_1 . É comprovado fazendo alterações nos dois primeiros elementos de books_2 e, em seguida, verificando esses mesmos elementos de book_1 em um TextBox .

books_1[0].title and books_2[1].title foram alterados para os novos valores de books_2[0].title and books_2[1].title .

AGORA A PERGUNTA

Como criamos uma nova cópia impressa de uma List ? A ideia é que books_1 e books_2 se tornem completamente independentes uns dos outros.

Estou decepcionado que a Microsoft não ofereceu uma solução simples, rápida e fácil como Ruby está fazendo com o método clone() .

O que seria realmente incrível de ajudantes é usar o meu código e alterá-lo com uma solução viável para que possa ser compilado e trabalhe. Acho que isso realmente ajudará os novatos a tentar entender as soluções oferecidas para esse problema.

EDIT: Observe que a class Book pode ser mais complexa e ter mais propriedades. Eu tentei manter as coisas simples.

Você precisa criar novos objects Book e colocá-los em uma nova List :

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

Atualização: um pouco mais simples … List tem um método chamado ConvertAll que retorna uma nova lista:

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

Crie uma ICloneable genérica ICloneable que você implementa em sua class Book para que a class saiba como criar uma cópia de si mesma.

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

Você pode então usar os methods linq ou ConvertAll que o Mark mencionou.

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

ou

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

Estou decepcionado que a Microsoft não ofereceu uma solução simples, rápida e fácil como Ruby está fazendo com o método clone() .

Exceto que não cria uma cópia profunda, ela cria uma cópia superficial.

Com cópia profunda, você tem que ser sempre cuidadoso, o que exatamente você quer copiar. Alguns exemplos de possíveis problemas são:

  1. Ciclo no gráfico de objects. Por exemplo, o Book tem um Author e um Author tem uma lista de seus Book .
  2. Referência a algum object externo. Por exemplo, um object pode conter o Stream aberto que grava em um arquivo.
  3. Eventos Se um object contiver um evento, praticamente qualquer um poderia se inscrever nele. Isso pode se tornar especialmente problemático se o assinante for algo como uma Window GUI.

Agora, existem basicamente duas maneiras de clonar algo:

  1. Implemente um método Clone() em cada class que você precisa clonar. (Há também uma interface ICloneable , mas você não deve usar isso; usar uma ICloneable customizada do ICloneable como Trevor sugeriu está bem.) Se você sabe que tudo que você precisa é criar uma cópia superficial de cada campo desta class, poderia usar MemberwiseClone() para implementá-lo. Como alternativa, você poderia criar um “construtor de cópias”: public Book(Book original) .
  2. Use serialização para serializar seus objects em um MemoryStream e, em seguida, desserialize-os de volta. Isso requer que você marque cada class como [Serializable] e também pode ser configurado o que exatamente (e como) deve ser serializado. Mas isso é mais uma solução “rápida e suja” e provavelmente também terá menos desempenho.

Bem,

Se você marcar todas as classs envolvidas como serializáveis, você pode:

 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/pt-PT/5c9b4c31-850d-41c4-8ea3-fae734b348c4/copy-listsomeobject-to-clone-list?forum=csharpgeneral

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

Isso deve fazer exatamente o que você quer. Demonstrado aqui.

Como o Clone retornaria uma instância de object do Book, esse object precisaria ser convertido em um livro antes de você poder chamar ToList nele. O exemplo acima precisa ser escrito como:

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

Se a class Array atender às suas necessidades, você também poderá usar o método List.ToArray, que copia elementos para uma nova matriz.

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