C# 如何创建 List<T> 的新深层副本(克隆)?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14007405/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 10:21:56  来源:igfitidea点击:

How create a new deep copy (clone) of a List<T>?

c#listclonedeep-copy

提问by TheScholar

In the following piece of 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;
        }
    }

}

I use a Bookobject type to create a List<T>and I populate it with a few items giving them a unique title (from 'one' to 'five').

我使用一个Book对象类型来创建一个,List<T>并用一些项目填充它,给它们一个独特的标题(从“一”到“五”)。

Then I create List<Book> books_2 = new List<Book>(books_1).

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

From this point, I know it's a clone of the list object, BUT the book objects from book_2are still a reference from the book objects in books_1. It's proven by making changes on the two first elements of books_2, and then checking those same elements of book_1in a TextBox.

从这一点来说,我知道这是列表对象的副本,但是从书的对象book_2仍然是一个参考从书的对象books_1。它是通过使上的两个第一要素的变化证明books_2,然后检查这些相同的元素book_1TextBox

books_1[0].title and books_2[1].titlehave indeed been changed to the new values of books_2[0].title and books_2[1].title.

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

NOW THE QUESTION

现在的问题

How do we create a new hard copy of a List<T>? The idea is that books_1and books_2become completely independent of each other.

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

I'm disappointed Microsoft didn't offer a neat, fast and easy solution like Ruby are doing with the clone()method.

我很失望微软没有像 Ruby 那样提供简洁、快速和简单的解决方案clone()

What would be really awesome from helpers is to use my code and alter it with a workable solution so it can be compiled and work. I think it will truly help newbies trying to understand offered solutions for this issue.

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

EDIT: Note that the Bookclass could be more complex and have more properties. I tried to keep things simple.

编辑:请注意,Book该类可能更复杂并具有更多属性。我试图让事情变得简单。

采纳答案by Mark Byers

You need to create new Bookobjects then put those in a new List:

您需要创建新Book对象,然后将它们放入新对象中List

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

Update: Slightly simpler... List<T>has a method called ConvertAllthat returns a new list:

更新:稍微简单一点......List<T>有一个方法调用ConvertAll返回一个新列表:

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

回答by Trevor Pilley

Create a generic ICloneable<T>interface which you implement in your Bookclass so that the class knows how to create a copy of itself.

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

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

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

You can then use either the linq or ConvertAllmethods that Mark mentioned.

然后,您可以使用ConvertAllMark 提到的 linq 或方法。

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

or

或者

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

回答by svick

I'm disappointed Microsoft didn't offer a neat, fast and easy solution like Ruby are doing with the clone()method.

我很失望微软没有像 Ruby 那样提供简洁、快速和简单的解决方案clone()

Except that does notcreate a deep copy, it creates a shallow copy.

除了创建深拷贝,它创建一个浅拷贝。

With deep copying, you have to be always careful, what exactly do you want to copy. Some examples of possible issues are:

对于深度复制,您必须始终小心,您到底要复制什么。可能出现的问题的一些示例是:

  1. Cycle in the object graph. For example, Bookhas an Authorand Authorhas a list of his Books.
  2. Reference to some external object. For example, an object could contain open Streamthat writes to a file.
  3. Events. If an object contains an event, pretty much anyone could be subscribed to it. This can get especially problematic if the subscriber is something like a GUI Window.
  1. 对象图中的循环。例如,Book有一个Author并且Author有他的Books的列表。
  2. 对某些外部对象的引用。例如,一个对象可能包含Stream写入文件的open 。
  3. 事件。如果一个对象包含一个事件,几乎任何人都可以订阅它。如果订阅者是 GUI 之类的东西,这会变得尤其成问题Window

Now, there are basically two ways how to clone something:

现在,基本上有两种方法可以克隆一些东西:

  1. Implement a Clone()method in each class that you need cloned. (There is also ICloneableinterface, but you should notuse that; using a custom ICloneable<T>interface as Trevor suggested is okay.) If you know that all you need is to create a shallow copy of each field of this class, you could use MemberwiseClone()to implement it. As an alternative, you could create a “copy constructor”: public Book(Book original).
  2. Use serialization to serialize your objects into a MemoryStreamand then deserialize them back. This requires you to mark each class as [Serializable]and it can also be configured what exactly (and how) should be serialized. But this is more of a “quick and dirty” solution, and will most likely also be less performant.
  1. Clone()在您需要克隆的每个类中实现一个方法。(也有ICloneable接口,但你应该不会使用;使用自定义的ICloneable<T>界面,特雷弗建议是好的。)如果你知道所有你需要的是创建该类的每个字段的一个浅拷贝,你可以使用MemberwiseClone()来实现它. 作为替代方案,你可以创建一个“拷贝构造函数”: public Book(Book original)
  2. 使用序列化将您的对象序列化为 a MemoryStream,然后将它们反序列化回来。这要求您将每个类标记为[Serializable],并且还可以配置应该序列化的确切内容(以及方式)。但这更像是一种“快速而肮脏”的解决方案,而且很可能性能也较低。

回答by Nate

Since Clonewould return an object instance of Book, that object would first need to be cast to a Book before you can call ToListon it. The example above needs to be written as:

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

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

回答by JHaps

If the Array class meets your needs, you could also use the List.ToArray method, which copies elements to a new array.

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

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

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

回答by gatsby

Well,

好,

If you mark all involved classes as serializable you can :

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

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

Source:

来源:

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

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

回答by Virepri

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

That should do exactly what you want. Demonstrated here.

那应该完全符合您的要求。在这里展示。

回答by Kanad Mehta

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

回答by Thao Le

You can use this:

你可以使用这个:

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

回答by Kristijonas Grigorovi?ius

Straight forward simple way to copy any generic list :

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

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