C#原型模式(深拷贝、浅拷贝)
原型模式就是用于创建重复的对象,当想要创建一个新的对象但是开销比较大或者想将对象的当前状态保存下来的时候,我们就可以使用原型模式。
创建原型
public abstract class Base { //因为String的特殊性,所以此次演示我们使用StringBuilder public StringBuilder Name { get; set; } public int Age { get; set; } public Base() { //模拟创建对象花费的开销 Thread.Sleep(1000); } public Base(String name, int age) { this.Name = new StringBuilder(name); this.Age = age; //模拟创建对象花费的开销 Thread.Sleep(1000); } //深拷贝 public abstract Base Clone(); //浅拷贝 public abstract Base MClone(); }
接下来创建一个Peron类,继承Base,并且实现两个复制方法
//如果是要通过序列化来进行深拷贝的话,要打上Serializable标签 [Serializable] public class Person : Base { public Person() : base() { } public Person(String name, int age) : base(name, age) { } /// <summary> /// 深拷贝 /// </summary> /// <returns>返回一个全新的Person对象</returns> public override Base Clone() { //创建一个内存流 MemoryStream ms = new MemoryStream(); //创建一个二进制序列化对象 BinaryFormatter bf = new BinaryFormatter(); //将当前对象序列化写入ms内存流中 bf.Serialize(ms, this); //设置流读取的位置 ms.Position = 0; //将流反序列化为Object对象 return bf.Deserialize(ms) as Person; } /// <summary> /// 浅拷贝 /// </summary> /// <returns></returns> public override Base MClone() => //浅拷贝 this.MemberwiseClone() as Person; }
Main方法中调用,首先我们每次都创建新的Person对象
static void Main(string[] args) { //用于计时 Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); Person p = new Person("a",28); Person p2 = new Person("a",28); Person p3 = new Person("a",28); stopwatch.Stop(); Console.WriteLine("耗时:"+stopwatch.Elapsed); Console.ReadKey(); }
运行结果:
可见如果创建对象如果开销很大的话,每次用的时候都创建效率就会很低
接下来我们使用原型模式来创建重复的对象,调用MClone()浅拷贝
static void Main(string[] args) { //用于计时 Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); Person p = new Person("a",10); Person p1 = p.MClone() as Person; Person p2 = p.MClone() as Person; //记录下来name的值,后续通过即时窗口查看 StringBuilder name2 = p2.Name; StringBuilder name1 = p1.Name; stopwatch.Stop(); Console.WriteLine("耗时:"+stopwatch.Elapsed); Console.ReadKey(); }
在Console.ReadKey();处设置断点,运行程序,打开 调试>>窗口>>即时,在右下角即时窗口输入 *&name1 回车,*&name2 回车,查看name1和name2的内存地址
我们可以看到,name1和name2的内存地址都是相同的,说明p2.Name和p1.Name是指向了同一个引用。所以对于属性是引用类型的对象,实现浅拷贝,所有的属性引用只会指向同一个对象,也就是说只要有一个对象修改了Name属性,其他的对象的Name属性都会发生改变。
看一下运行结果
可以发现,只有第一次创建对象需要很大的开销,通过原型复制的话是很快的。
接下来我们看一下深拷贝
static void Main(string[] args) { //用于计时 Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); Person p = new Person("a",10); Person p1 = p.Clone() as Person; Person p2 = p.Clone() as Person; //记录下来name的值,后续通过即时窗口查看 StringBuilder name2 = p2.Name; StringBuilder name1 = p1.Name; stopwatch.Stop(); Console.WriteLine("耗时:"+stopwatch.Elapsed); Console.ReadKey(); }
执行和刚才相同的操作,来看一下内存地址
可以发现,p1和p2的Name在内存中的地址是不一样的, 就是说明深拷贝会将对象的所有非静态属性都复制一份,如果碰到引用类型也会重新创建一份,而不是复制指向对象的引用。
最后我们看一下运行结果