web-dev-qa-db-ja.com

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

}

Bookオブジェクトタイプを使用してList<T>を作成し、一意のタイトル(「1」から「5」)を付与するいくつかのアイテムを入力します。

次に、List<Book> books_2 = new List<Book>(books_1)を作成します。

この時点から、リストオブジェクトのクローンであることがわかりますが、book_2のブックオブジェクトは、books_1のブックオブジェクトからの参照のままです。 books_2の最初の2つの要素に変更を加え、TextBoxbook_1の同じ要素をチェックすることで証明されます。

books_1[0].title and books_2[1].titlebooks_2[0].title and books_2[1].titleの新しい値に変更されました。

今の質問

List<T>の新しいハードコピーを作成する方法books_1books_2は互いに完全に独立するという考え方です。

Rubyがclone()メソッドで行っているような、きちんとした高速で簡単なソリューションをMicrosoftが提供していなかったことに失望しています。

ヘルパーから本当に素晴らしいことは、私のコードを使用し、実行可能なソリューションでそれを変更して、コンパイルして動作できるようにすることです。この問題に対して提供されているソリューションを理解しようとする初心者にとって、本当に役立つと思います。

編集:Bookクラスはより複雑でプロパティが多いことに注意してください。物事をシンプルにしようとしました。

58
TheScholar

新しいBookオブジェクトを作成してから、それらを新しいListに入れる必要があります。

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

更新:少し簡単です... List<T>には、新しいリストを返す ConvertAll というメソッドがあります。

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

Bookクラスに実装する汎用ICloneable<T>インターフェイスを作成して、クラスが自身のコピーを作成する方法を認識できるようにします。

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

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

その後、Markが言及したlinqまたはConvertAllメソッドを使用できます。

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

または

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

Rubyがclone()メソッドで行っているようなきちんとした、高速で簡単なソリューションをMicrosoftが提供していなかったことに失望しています。

ディープコピーを作成するnot以外は、シャローコピーを作成します。

ディープコピーでは、常に何をコピーするのか、常に注意する必要があります。考えられる問題の例を次に示します。

  1. オブジェクトグラフのサイクル。たとえば、BookにはAuthorがあり、AuthorにはBooksのリストがあります。
  2. 外部オブジェクトへの参照。たとえば、オブジェクトには、ファイルに書き込むopen Streamを含めることができます。
  3. イベント。オブジェクトにイベントが含まれる場合、ほとんど誰でもそのイベントにサブスクライブできます。サブスクライバーがGUI Windowのようなものである場合、これは特に問題になる可能性があります。

現在、基本的に何かを複製する方法は2つあります。

  1. クローンが必要な各クラスにClone()メソッドを実装します。 (ICloneableインターフェースもありますが、notを使用する必要があります。Trevorが提案したカスタムICloneable<T>インターフェースを使用しても大丈夫です。)必要なのがこのクラスの各フィールドの浅いコピーを作成することだけであることがわかっている場合、 MemberwiseClone() を使用して実装できます。別の方法として、「コピーコンストラクター」を作成することもできます:public Book(Book original)
  2. シリアル化を使用してオブジェクトをMemoryStreamにシリアル化してから、逆シリアル化して戻します。これには、各クラスを[Serializable]としてマークする必要があります。また、正確に何を(およびどのように)シリアル化するかを構成することもできます。しかし、これは「迅速で汚れた」ソリューションであり、ほとんどの場合パフォーマンスも低下します。
17
svick
List<Book> books_2 = new List<Book>(books_2.ToArray());

それはまさにあなたが望むことをする必要があります。 ここで説明します

8
Virepri

まあ、

関係するすべてのクラスをシリアライズ可能としてマークすると、次のことができます。

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

ソース:

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

7
gatsby

CloneはBookのオブジェクトインスタンスを返すため、ToListを呼び出す前に、そのオブジェクトをBookにキャストする必要があります。上記の例を次のように記述する必要があります。

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

Arrayクラスがニーズを満たしている場合は、List.ToArrayメソッドを使用して、要素を新しい配列にコピーすることもできます。

参照: http://msdn.Microsoft.com/en-us/library/x303t819(v = vs.110).aspx

2
JHaps

これを使用できます:

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