克隆列表
我认为要克隆一个List你只需要调用:
List cloneList = new List(originalList);
但我在我的代码中尝试了这一点,我似乎得到的效果暗示上面只是在做:
cloneList = originalList …因为对cloneList的更改似乎正在影响originalList。
那么克隆List的方法是什么?
编辑:
我在考虑做这样的事情:
public static List Clone(this List originalList) where T : ICloneable { return originalList.ConvertAll(x => (T) x.Clone()); }
EDIT2:
我拿了Binoj Antony建议的深拷贝代码并创建了这个扩展方法:
public static T DeepCopy(this T original) where T : class { using (MemoryStream memoryStream = new MemoryStream()) { BinaryFormatter binaryFormatter = new BinaryFormatter(); binaryFormatter.Serialize(memoryStream, original); memoryStream.Seek(0, SeekOrigin.Begin); return (T)binaryFormatter.Deserialize(memoryStream); } }
EDIT3:
现在,假设列表中的项目是结构。 如果我打电话会怎么样?:
List cloneList = new List(originalList);
我非常肯定,我会得到一个填充了新的独特项目的列表,对吗?
您可以使用以下代码制作列表的深层副本或支持序列化的任何其他对象:
此外,您可以将此用于v 2.0及更高版本的任何.NET框架版本,并且可以应用类似的技术(删除generics的使用)并在1.1中使用
public static class GenericCopier { public static T DeepCopy(object objectToCopy) { using (MemoryStream memoryStream = new MemoryStream()) { BinaryFormatter binaryFormatter = new BinaryFormatter(); binaryFormatter.Serialize(memoryStream, objectToCopy); memoryStream.Seek(0, SeekOrigin.Begin); return (T) binaryFormatter.Deserialize(memoryStream); } } }
你可以用它来调用它
List deepCopiedList = GenericCopier>.DeepCopy(originalList);
测试是否有效的完整代码:
static void Main(string[] args) { List originalList = new List (5); Random random = new Random(); for(int i = 0; i < 5; i++) { originalList.Add(random.Next(1, 100)); Console.WriteLine("List[{0}] = {1}", i, originalList[i]); } List deepCopiedList = GenericCopier>.DeepCopy(originalList); for (int i = 0; i < 5; i++) Console.WriteLine("deepCopiedList[{0}] value is {1}", i, deepCopiedList[i]); }
这会奏效……
List cloneList = new List (originalList);
当你说“因为对cloneList的更改似乎正在影响originalList”。 – 您的意思是更改列表 ,或更改项目 …
添加/删除/交换项目正在更改列表 – 因此,如果我们这样做:
cloneList.Add(anotherItem);
你会发现cloneList
比originalList
。 但是,如果内容是引用类型(类),那么两个列表仍然指向相同的底层对象 – 所以如果我们这样做:
cloneList[0].SomeObjectProperty = 12345;
然后这也将显示对originalList[0].SomeObjectProperty
– 只有一个对象(在两个列表之间共享)。
如果这是问题,你将不得不克隆列表中的对象 – 然后你进入整个深层和浅层问题……这是问题吗?
对于浅层副本,您可以使用非常类似于答案的内容 – 只需使用TTo = TFrom
(可能简化为单个T
)。
我怀疑你的实际例子会有问题,因为int
是一个值类型。 例如:
using System; using System.Collections.Generic; class Test { static void Main() { List originalList = new List { 5, 6, 7 }; List cloneList = new List (originalList); cloneList.Add(8); cloneList[0] = 2; Console.WriteLine(originalList.Count); // Still 3 Console.WriteLine(originalList[0]); // Still 5 } }
但是,正如Marc所说,如果您的列表包含可变引用类型,则克隆列表将只采用浅表副本 – 如果您改变列表引用的对象,则这些更改将通过两个列表可见。 替换一个列表中的元素不会更改另一个列表中的等效元素:
using System; using System.Collections.Generic; class Dummy { public int Value { get; set; } public Dummy (int value) { this.Value = value; } } class Test { static void Main() { List originalList = new List { new Dummy(5), new Dummy(6), new Dummy(7) }; List cloneList = new List (originalList); cloneList[0].Value = 1; cloneList[1] = new Dummy(2); Console.WriteLine(originalList[0].Value); // Changed to 1 Console.WriteLine(originalList[1].Value); // Still 6 } }
要对元素类型实现ICloneable
的列表进行“深度克隆”,请使用:
List cloneList = originalList.ConvertAll(x => (Foo) x.Clone());
然而,这个克隆的真正深度将取决于元素类型中ICloneable
的实现 – ICloneable
通常被认为是一件坏事,因为它的合同是如此模糊。
如果列表的基础类型是值类型 ,则使用带有原始列表作为参数的List构造函数将起作用。 对于引用类型List元素,我想你想要深层复制它们。
你可以这样做:
(假设底层类型实现了ICloneable)
originalList.ForEach((item) => { cloneList.Add((ICloneable)item.Clone()); } );
或者使用一些LINQ :
var cloneList = originalList.Select(item => (ICloneable)item.Clone()).ToList();
我投票不依赖于对象序列化。 这是昂贵而糟糕的做法。
public static TObj CloneObject(this TObj obj) where TObj : ICloneable { return (TObj)obj.Clone(); }
上面的方法更优雅,如果需要,你应该真正关心实现一个可克隆的接口。 你也可以把它变成通用的。
public interface ICloneable : IClonable { T CloneObject(); }
或者,您可以避免将IClonable接口用作基本类型,因为它维护得很差。 方法名称必须更改为,因为您不能对返回类型执行重载。
public static List CloneList(this List source) where TObj : ICloneable { return source.Select(x=>x.CloneObject()).ToList(); }
就这么简单。
也许你的问题可以通过使用值类型来解决。 它们总是按副本复制。 因此,只要您的数据结构是按值计算,就不必克隆任何内容。
它特别说明了项目被复制到新列表中。 是的,那应该有用。 使用值类型,您将获得完全独立性。 但请记住,使用引用类型,列表将是独立的,但它们将指向相同的对象。 您需要深层复制列表。
List list = new List (); List clone = new List (list); list.Add (new int ()); Debug.Assert (list != clone); Debug.Assert (list.Count == 1); Debug.Assert (clone.Count == 0);
这段代码非常适合我。 你是否可以更改列表中的对象? new List(oldList)
不会克隆列表项。
我必须补充一点:如果您要使用序列化来促进深度复制,为什么要克隆每个单独的项目? 只需克隆整个原始列表即可。
除非您具有仅克隆满足特定条件的节点的逻辑,否则逐节点地执行此操作。