以前一直在写C++,最近玩起了C#,才发现C#的深拷贝好麻烦。
问题
我在程序的一个类Class1里新建了一个List,用于存放另一个类Class2的实例,而Class2中另有一个List。然后某些操作需要将其中一个Class1实例的数据拷贝到另一个实例当中。我本以为用类似C++的vector的拷贝方法就可以:
a.list = new List<Class2>(b.list);
结果我在改变b.list当中的List内某个元素的值时,发现a.list里面对应元素的值也改变了。原因是以上方法只是浅拷贝,只拷贝了引用。
解释
C#里有两种数据类型,一种是值类型,如int等基础类型,而同时struct也是直接值传递的;一种是引用类型,如自定义的类,包括string其实也是引用类型,只不过内部做了一些处理。当然,struct里面有引用类型的话,这些引用类型还是只传了引用。new List<Class>(list)
也只不过是将list里的每个元素的引用传给了新的List,因此改变其中一个必然导致另外一个改变。
深拷贝
网上有许多深拷贝的方法,什么序列化、反射等等,暂时还看不懂。倒是有一个方法比较简便,就是自定义的类继承ICloneable这个基类,然后在类内部实现public object Clone()
方法。类结构大致如下:
class Class2 : ICloneable
{
List<int> list;
...
public object Clone()
{
Class2 newObject = new Class2(); //新建一个实例
newObject.list = new List<int>(list);
return newObject;
}
}
以上代码里Class2的list里是值类型,可以直接用new List<int>(list)
来拷贝。到了Class1里,情况就不一样了:
class Class1 : ICloneable
{
List<Class2> list;
...
public object Clone()
{
Class1 newObject = new Class1(); //新建一个实例
newObject.list = new List<int>(); //初始化一个List
//将本实例的list每个元素都深拷贝一份到新实例的list内
list.Foreach(i => newObject.list.Add(i.Clone()));
return newObject;
}
}
这样就可以完成深拷贝。拷贝构造函数里面还是继续用浅拷贝,而需要深拷贝时调用Clone()函数。