原型模式就是用於創建重復的對象,當想要創建一個新的對象但是開銷比較大或者想將對象的當前狀態保存下來的時候,我們就可以使用原型模式。
創建原型
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在內存中的地址是不一樣的, 就是說明深拷貝會將對象的所有非靜態屬性都復制一份,如果碰到引用類型也會重新創建一份,而不是復制指向對象的引用。
最后我們看一下運行結果