浅拷贝就是成员数据之间的一一赋值:把值一一赋给要拷贝的值。但是可能会有这样的情况:对象还包含资源,这里资源可以是堆,或者一个文件。当值拷贝的时候,两个对象就共用共同的资源,同时对资源访问。这样就可能出现问题。深拷贝就是把资源也赋值一次,使对象拥有自己的资源,而不是对象共有资源。
深拷贝:在拷贝对象的时候,为新对象制作了外部对象的独立拷贝。
浅拷贝:如果拷贝对象中引用了某个外部的内容,那么在拷贝这个对象的时候,让新旧两个对象指向同一个外部的内容,而不是各自拥有独立的资源。
深拷贝是指源对象与拷贝对象 互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。
考虑以下写法
int source = int .MaxValue;//(1) 初始化源对象为整数的最大值2,147,483,647
int dest = source;//(2) 赋值,内部执行深拷贝
dest = 1024;//(3) 对拷贝对象进行赋值
source = 2048;//(4) 对源对象进行赋值
首先( 2 )中将 source 赋给 dest ,执行了深拷贝动作,其时 dest 和 source 的值是一样的,都是 int.MaxValue ;( 3 )对 dest 进行修改, dest 值变为 1024 ,由于是深拷贝,因此不会运行 source , source 仍然是 int.MaxValue ;( 4 )对 source 进行了修改,同样道理, dest 仍然是 1024 ,同时 int.MaxValue 的值也不变,仍然是 2,147,483,647 ;只有 source 变成了 2048 。
再考虑以下写法
struct Point
{
public int X;
public int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
}
Point source = new Point (10, 20);
Point dest = source;
dest.X = 20
当dest.X 属性变成20 后,source 的X 属性仍然是10
2. 浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同)。对其中任何一个对象的改动都会影响另外一个对象。举个例子,一个人一开始叫张三,后来改名叫李四了,可是还是同一个人,不管是张三缺胳膊少腿还是李四缺胳膊少腿,都是这个人倒霉。比较典型的就有 Reference (引用)对象,如 Class (类)。
考虑以下写法
class Point
{
public int X;
public int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
}
Point source = new Point (10, 20);
Point dest = source;
dest.X = 20;
由于Point 现在是引用对象,因此Point dest=source 的赋值动作实际上执行的是浅拷贝,最后的结果应该是source 的X 字段值也变成了20 。即它们引用了同一个对象,仅仅是变量明source 和dest 不同而已。
3. 引用对象的浅拷贝原理
引用对象之间的赋值之所以执行的是浅拷贝动作,与引用对象的特性有关,一个引用对象一般来说由两个部分组成
( 1 )一个具名的 Handle ,也就是我们所说的声明(如变量)
( 2 )一个内部(不具名)的对象,也就是具名 Handle 的内部对象。它在 Manged Heap (托管堆)中分配,一般由新增引用对象的 New 方法是进行创建
如果这个内部对象已被创建,那么具名的 Handle 就指向这个内部对象在 Manged Heap 中的地址,否则就是 null (从某个方面来讲,如果这个具名的 handle 可以被赋值为 null ,说明这是一个引用对象,当然不是绝对)。两个引用对象如果进行赋值,它们仅仅是复制这个内部对象的地址,内部对象仍然是同一个,因此,源对象或拷贝对象的修改都会影响对方。这也就是浅拷贝
4. 引用对象如何进行深拷贝
由于引用对象的赋值仅仅是复制具名 Handle (变量)指向的地址,因此要对引用对象进行深拷贝就要重新创建一份该对象的实例,并对该对象的字段进行逐一赋值,如以下写法
class Point
{
public int X;
public int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
}
Point source = new Point (10, 20);
Point dest = new Point (source.X, source.Y);
// 或以下写法
//Point dest = new Point()
//dest.X = source.X
//dest.Y = source.Y
其时, source 和 dest 就是两个互相独立的对象了,两者的修改都不会影响对方
5 .一些需要注意的东西
( 1 ): String 字符串对象是引用对象,但是很特殊,它表现的如值对象一样,即对它进行赋值,分割,合并,并不是对原有的字符串进行操作,而是返回一个新的字符串对象
( 2 ): Array 数组对象是引用对象,在进行赋值的时候,实际上返回的是源对象的另一份引用而已;因此如果要对数组对象进行真正的复制(深拷贝),那么需要新建一份数组对象,然后将源数组的值逐一拷贝到目的对象中。