C#中的浅拷贝和深拷贝
[所谓拷贝,即通过某种方式获得已知C#对象的一个副本]问题一:什么是浅拷贝?
浅 -> 如果该对象中包含值类型和引用类型,那么拷贝后得到的新的对象值类型为原有对象中值类型的一个副本,该值存储在新的地址空间内;而新对象中的引用类型存储的地址仍指向原对象中引用类型所指向的对象的内存空间(即两个引用都指向同一块内存空间)
问题二:什么是深拷贝?
深 -> 与"浅"的不同之处在于拷贝得到的新对象中的引用类型所指向的对象虽然与原对象中引用类型所指向的对象具有相同的值,但是他们的内存空间是不同的(即新对象重新开辟了内存空间的来存储原对象中包含的引用类型对象)
问题三:如何实现浅拷贝?
浅拷贝的实现很简单,.NET Framework为我们提供了一个ICloneable接口,只要实现接口的Clone方法,并调用当前对象的MemberwiseClone()方法即可实现,当然你也可以在Clone对象中创建一个新的对象,并把this中的值一个个赋值给新的对象,此处引用类型也直接赋值即可。代码如下:
//实现ICloneable接口
class Person:ICloneable
{
public string Name{get;set;}
public int Age{get;set;}
//每个人都有一只猫
public Cat C{get;set;}
public object Clone()
{
return this.MemberwiseClone();
}
}
class Cat
{
public string Name{get;set;}
public override string ToString()
{
return "My cat's name is" + this.Name;
}
}
测试代码:static void Main(string[] args)
{
Person p = new Person() { Name = "Dream", Age = 18, C = new Cat() { Name="Catty"} };
Person pClone = p.Clone() as Person;
Console.WriteLine(pClone.C);
//浅拷贝:如果p.C发生改变,pClone.C也会发生同样的改变
p.C.Name = "Snoppy";
Console.WriteLine("改变p.C.Name后,pClone.C.Name的值:");
Console.WriteLine(pClone.C);
Console.ReadLine();
}
问题四:如何实现深拷贝?
比较笨一点的方法就是如果拷贝的对象中仍包含引用对象,那么调用该引用对象的MemberwiseClone()即可,而值对象只需要简单的赋值即可,但是如果一个对象中包含了太多的引用对象,岂不是要对每一个对象都调用MemberwiseClone()方法么?所以:
建议使用对象的序列化进行深拷贝!!!代码如下(只需要改动浅拷贝中的Clone方法即可,测试代码与浅拷贝中的测试代码一致):
public object Clone()
{
//使用内存流来传递数据
using (Stream stream = new MemoryStream())
{
IFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, this);
stream.Seek(0, SeekOrigin.Begin);
return formatter.Deserialize(stream) as Person;
}
}
注意:如果一个对象需要被序列化,必须在类上面写明可被序列化的标记代码建议:由于可以通过改写Clone方法来实现两种拷贝方式,为了区分拷贝的方式,只让Clone方法去实现一个浅拷贝,可以自己在类中定义深拷贝和浅拷贝的方法,并实现他们,在有需要的情况下。