书城计算机大话设计模式
11030400000132

第132章 9 克隆对象分为浅拷贝和深拷贝

小A:“克隆对象有哪些?”

大B:“克隆对象分为浅拷贝和深拷贝。我给你详细讲一下。浅拷贝就是克隆的对象和它的源对象共享引用的对象,举个例子,可能不恰当,假设牛肉刀削面引用一个对象:Money,表示它值多少钱,这里说的对象是说它是System。Object继承的(c#),也就是说不同的浅拷贝对象,他们的价钱是一样的,当克隆对象的价钱变过之后,它所引用的对象的价钱也就随之改变了,比如面馆。调低了牛肉刀削面的价格,由5块钱调到了4块钱,那么每碗面的价格都变到了4块钱。但对值类型而言,每个浅拷贝对象都有自己的拷贝,也就是说,当改变克隆对象的值类型时,它的源对象相应属性不会改变。深拷贝就是完全的拷贝。不仅值类型有自己的拷贝,连引用对象也有自己的一份拷贝,修改克隆对象的任何属性,也不会对源对象产生任何影响。原型管理器,就是提供原型注册使用,当创建对象使,可以使用里面的对象进行克隆,当有新的实例对象时,也可以将他们加到原型管理器里。说的有点乱,程序员,还是用代码交流最好。”

下面的程序分别实现了原型克隆时浅拷贝和深拷贝。

//

//理解深拷贝和浅拷贝

//

//浅表副本创建与原始对象具有相同类型的新实例,然后复制原始对象的非静态字段。

//如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制该

//引用但不复制被引用的对象;这样,原始对象中的引用和复本中的引用指向同一个对象。

//相反,对象的深层副本复制对象中字段直接或间接引用的全部内容。

//

//例如,如果X 是一个具有对对象A 和对象B 的引用的Object,并且对象A 还具

//有对对象M 的引用,则X 的浅表副本是对象Y,而Y 同样具有对对象A 和对象。

//的引用。相反,X 的深层副本是对象Y,而对象Y 具有对对象C 和对象D 的直接引

//用以及对对象N 的间接引用,其中C 是A 的副本,D 是B 的副本,而N 是M 的副本。

using System;

using System。Collections;

using System。IO;

using System。Runtime。Serialization。Formatters。Binary;

using System。Runtime。Serialization;

using System。Data;

namespace Prototype

{

///《summary》

///Prototype类型实例

///《/summary》

class TestPrototypeApp

{

///《summary》

///应用程序的主入口点。

///《/summary》

[STAThread]

static void Main(string[]args)

{

//定义原型管理器

NoodleManager noodleManager=new NoodleManager();

//客户要求下面三碗面

Noodle beefNoodle=(Noodle)noodleManager[“牛肉拉面”]。Clone();

//Noodle beefNoodle=(Noodle)noodleManager[“牛肉拉面”]。DeepClone();

Noodle muttonNoodle=(Noodle)noodleManager[“羊肉拉面”]。Clone();

Noodle beefCutNoodle=(Noodle)noodleManager[“牛肉刀削面”]。Clone();

//修改克隆对象中的引用对象的属性,验证它是浅拷贝还是深拷贝

beefNoodle。TbName=,“哈哈哈!克隆对象改名了,你改不改”;

//显示原始对象的NoodelName和TbName?

Console。WriteLine(noodleManager[“牛肉拉面”]。NoodleName noodleManager[“牛肉拉面”]。TbName “\n”);

//显示克隆对象的NoodleName和。

Console。WriteLine(beefNoodle。NoodleName beefNoodle。TbName “\n”);

//将新的产品加入原型管理器,以备以后克隆时使用,下面是定义了一种新的面条-羊肉刀削面,

//并把它添加到面条管理器中,如果以后再有客户点这个面,直接克隆即可。

noodleManager[“羊肉刀削面”]=new CutNoodle(“羊肉刀削面”);

//克隆一碗羊肉刀削面

Noodle muttonCutNoodle=(Noodle)noodleManager[“羊肉刀削面”]。Clone();

Console。WriteLine(noodleManager[“羊肉刀削面”]。NoodleName “\n”);

Console。WriteLine(muttonCutNoodle。NoodleName “\n”);

Console。ReadLine();

}

}

//抽象产品-面条

//序列化属性,为深拷贝时使用,每个派生类都要加上此属性才能实现深拷贝

[Serializable]

public abstract class Noodle

{

//定义一个DataTable对象,主要是为了验证对比类中含有引用对象时的深拷贝和浅拷贝时的不同,

//你也可以采用别的任何引用对象

protected DataTable dataTable=new DataTable();

public string TbName

{

get{return dataTable。TableName;}

set{dataTable。TableName=value;}

}

//字段

protected string noodleName;

//特性

public string NoodleName

{

get{return noodleName;}

set{noodleName=value;}

}

public abstract Noodle Make(string name);

//浅克隆的接口

public abstract Noodle Clone();

//深克隆的接口

public abstract Noodle DeepClone();

}

//具体产品,拉面

[Serializable]

public class PullNoodle:Noodle

{

public PullNoodle(string name)

{

this。NoodleName=name;

this。TbName=name “table”;

Console。WriteLine(“PullNoodle is made\n”);

}

//实现浅拷贝

public override Noodle Clone()

{

return(Noodle)this。MemberwiseClone();

}

//实现深拷贝,“淹咸菜”的过程,先将对象序列化到内存流,再反序列化,即可得到深克隆

public override Noodle DeepClone()

{

//定义内存流

MemoryStream ms=new MemoryStream();

//定义二进制流

IFormatter bf=new BinaryFormatter();

//序列化

bf。Serialize(ms,this);

//重置指针到起始位置,以备反序列化

ms。Position=0;

//返回反序列化的深克隆对象

return(Noodle)bf。Deserialize(ms);

}

public override Noodle Make(string name)

{

return new PullNoodle(name);

}

}

//具体产品-刀削面

[Serializable]

public class CutNoodle:Noodle

{

public CutNoodle(string name)

{

this。NoodleName=name;

this。TbName=name “table”;

Console。WriteLine(“CutNoodle is made\n”);

}

//实现浅克隆

public override Noodle Clone()

{

return(Noodle)this。MemberwiseClone();

}

public override Noodle Make(string name)

{

return new CutNoodle(name);

}

//实现深克隆

public override Noodle DeepClone()

{

MemoryStream ms=new MemoryStream();

IFormatter bf=new BinaryFormatter();

bf。Serialize(ms,this);

ms。Position=0;

return(Noodle)bf。Deserialize(ms);

}

}

//定义原型管理器,用于存储原型集合,这里采用的是。

class NoodleManager

{

//定义。

protected Hashtable noodleHt=new Hashtable();

protected Noodle noodle;

public NoodleManager()

{

//初始化时加入三种基本原型

noodle=new PullNoodle(“牛肉拉面”);

noodleHt。Add(“牛肉拉面”,noodle);

noodle=new PullNoodle(“羊肉拉面”);

noodleHt。Add(“羊肉拉面”,noodle);

noodle=new CutNoodle(“牛肉刀削面”);

noodleHt。Add(“牛肉刀削面”,noodle);

}

//索引器,用于添加,访问Noodle对象

public Noodle this[string key]

{

get{return(Noodle)noodleHt[key];}

set{noodleHt。Add(key,value);}

}

}

}