MemberwiseClone 方法創建一個淺表副本,具體來說就是創建一個新對象,然後將當前對象的非靜態字段複製到該新對象。如果字段是值類型的,則對該字段執行逐位複製。如果字段是引用類型,則複製引用但不復制引用的對象;因此,原始對象及其複本引用同一對象。
爲了實現深度複製,我們就必須遍歷有相互引用的對象構成的圖,並需要處理其中的循環引用結構。這無疑是十分複雜的。幸好藉助.Net的序列化和反序列化機制,可以十分簡單的深度Clone一個對象。原理很簡單,首先將對象序列化到內存流中,此時對象和對象引用的所用對象的狀態都被保存到內存中。.Net的序列化機制會自動處理循環引用的情況。然後將內存流中的狀態信息反序列化到一個新的對象中。這樣一個對象的深度複製就完成了。在原型設計模式中CLONE技術非常關鍵。
下面的代碼就是演示這個問題:
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace CloneDemo
{
[Serializable]
class DemoClass
{
public int i = 0;
public int[] iArr = { 1, 2, 3 };
public DemoClass Clone1() //淺CLONE
{
return this.MemberwiseClone() as DemoClass;
}
public DemoClass Clone2() //深clone
{
MemoryStream stream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, this);
stream.Position = 0;
return formatter.Deserialize(stream) as DemoClass;
}
}
class Program
{
static void Main(string[] args)
{
DemoClass a = new DemoClass();
a.i = 10;
a.iArr = new int[] { 8, 9, 10 };
DemoClass b = a.Clone1();
DemoClass c = a.Clone2();
// 更改 a 對象的iArr[0], 導致 b 對象的iArr[0] 也發生了變化 而 c不會變化
a.iArr[0] = 88;
Console.WriteLine("MemberwiseClone");
Console.WriteLine(b.i);
foreach (var item in b.iArr)
{
Console.WriteLine(item);
}
Console.WriteLine("Clone2");
Console.WriteLine(c.i);
foreach (var item in c.iArr)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
}
}