在下面的代码中,
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_1
和books_2
完全相互独立。
我很失望微软没有像Ruby那样提供一个简洁,快速,简单的解决方案clone()
方法。
帮助者真正棒极了的是使用我的代码并使用可行的解决方案来改变它,以便可以编译和工作。我认为这将真正帮助新手尝试理解为此问题提供的解决方案。
编辑:请注意,Book
类可能更复杂,并且具有更多属性。我试着保持简单。
答案 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()
方法。
除非不创建深层副本,否则会创建浅层副本。
通过深度复制,您必须始终小心,您想要复制的内容。可能的问题的一些例子是:
Book
有一个Author
,Author
有一个Book
的列表。Stream
。Window
,则会出现问题。现在,基本上有两种克隆方法:
Clone()
方法。 (还有ICloneable
界面,但你应该不使用它;使用自定义ICloneable<T>
界面,如Trevor建议的那样好。)如果你知道你需要的只是创建此类的每个字段的浅表副本,您可以使用MemberwiseClone()
来实现它。作为替代方案,您可以创建“复制构造函数”:public Book(Book original)
。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);
}
来源:
答案 5 :(得分:4)
C# 9 records 和 with 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