如何创建List <t>的新深层副本(克隆)?</t>

时间:2012-12-22 23:18:41

标签: c# list clone deep-copy

在下面的代码中,

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>,并使用一些项目填充它,为其提供唯一标题(从&#39;一个&#39;到&#39;五& #39;)

然后我创建List<Book> books_2 = new List<Book>(books_1)

从这一点来说,我知道它是列表对象的克隆,但是来自book_2的书籍对象仍然是books_1中书籍对象的引用。通过对books_2的前两个元素进行更改,然后在book_1中检查TextBox的相同元素,证明了这一点。

books_1[0].title and books_2[1].title确实已更改为books_2[0].title and books_2[1].title的新值。

现在的问题

我们如何创建List<T>的新硬拷贝?我们的想法是books_1books_2完全相互独立。

我很失望微软没有像Ruby那样提供一个简洁,快速,简单的解决方案clone()方法。

帮助者真正棒极了的是使用我的代码并使用可行的解决方案来改变它,以便可以编译和工作。我认为这将真正帮助新手尝试理解为此问题提供的解决方案。

编辑:请注意,Book类可能更复杂,并且具有更多属性。我试着保持简单。

11 个答案:

答案 0 :(得分:93)

您需要创建新的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));

答案 1 :(得分:30)

创建一个在ICloneable<T>类中实现的通用Book接口,以便该类知道如何创建自己的副本。

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

答案 2 :(得分:17)

  

我很失望微软没有像Ruby那样提供一个简洁,快速,简单的解决方案clone()方法。

除非创建深层副本,否则会创建浅层副本。

通过深度复制,您必须始终小心,您想要复制的内容。可能的问题的一些例子是:

  1. 在对象图中循环。例如,Book有一个AuthorAuthor有一个Book的列表。
  2. 引用某些外部对象。例如,对象可以包含写入文件的开放Stream
  3. 事件。如果一个对象包含一个事件,几乎任何人都可以订阅它。如果订户类似于GUI Window,则会出现问题。
  4. 现在,基本上有两种克隆方法:

    1. 在每个需要克隆的类中实现Clone()方法。 (还有ICloneable界面,但你应该使用它;使用自定义ICloneable<T>界面,如Trevor建议的那样好。)如果你知道你需要的只是创建此类的每个字段的浅表副本,您可以使用MemberwiseClone()来实现它。作为替代方案,您可以创建“复制构造函数”:public Book(Book original)
    2. 使用序列化将对象序列化为MemoryStream,然后将其反序列化。这要求您将每个类标记为[Serializable],并且还可以配置应该序列化的确切(以及如何)。但这更像是一种“快速而肮脏”的解决方案,并且很可能也会降低性能。

答案 3 :(得分:8)

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

这应该完全符合你的要求。 Demonstrated here.

答案 4 :(得分:7)

那么,

如果您将所有涉及的类标记为可序列化,则可以:

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

答案 5 :(得分:4)

C# 9 recordswith expressions 可以使它更容易一些,尤其是当您的类型具有许多属性时。

您可以使用以下内容:

get the numberOfDaysInMonth(current date)
        --> 31

get the numberOfDaysInMonth(December)
        --> 31

get the numberOfDaysInMonth(2)
        --> 28

我以示例为例:

var books2 = books1.Select(b => b with { }).ToList();

并且输出是:(对 Books2 中的对象所做的更改不会影响 Books1 中的原始对象)

<块引用>

Books1 包含:

Book { Name = Book1.1 }

Book { Name = Book1.2 }

Book { Name = Book1.3 }

Books2 包含:

预订{名称=已更改}

预订{名称=已更改}

Book { Name = Book1.3 }

答案 6 :(得分:2)

由于Clone将返回Book的对象实例,因此首先需要将该对象强制转换为Book,然后才能在其上调用ToList。上面的例子需要写成:

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

答案 7 :(得分:2)

如果Array类满足您的需要,您还可以使用List.ToArray方法,该方法将元素复制到新数组。

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

答案 8 :(得分:1)

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

答案 9 :(得分:-1)

您可以使用此:

var newList= JsonConvert.DeserializeObject<List<Book>>(list.toJson());

答案 10 :(得分:-2)

直接复制任何通用列表的简单方法:

List<whatever> originalCopy=new List<whatever>();//create new list
originalCopy.AddRange(original);//perform copy of original list