C# 深淺複製 MemberwiseClone
學無止境,精益求精
十年河東,十年河西,莫欺少年窮
學歷代表你的過去,能力代表你的現在,學習代表你的將來
最近拜讀了大話設計模式:原型模式,該模式主要應用C# 深淺複製來實現的!關於深淺複製大家可參考MSDN:
https://group.jd.com/thread/20000001/26285684/20000004.htm
所謂深淺複製可解讀為:
淺複製:在C#中呼叫MemberwiseClone() 方法即為淺複製。如果欄位是值型別的,則對欄位執行逐位複製,如果欄位是引用型別的,則複製物件的引用,而不復制物件,因此:原始物件和其副本引用同一個物件!
深複製:如果欄位是值型別的,則對欄位執行逐位複製,如果欄位是引用型別的,則把引用型別的物件指向一個全新的物件!
上述的解釋可能看不太懂,我們作如下案例進行分析:
View Code上述程式碼分析如下:
原始物件P1,通過淺複製得到物件P2,通過深複製得到P3
原始物件P1中的值型別屬性有:Age 和 Name ,引用型別物件有:IdInfo
根據上述淺複製的概念可知:P2中的Age 和 Name 相對於 P1是全新的,但P2中的 IdInfo 和 P1中的 IdInfo 是同一個物件,二者同在一個記憶體地址!
根據上述深複製的概念可知:P3中的Age 和 Name 相對於 P1是全新的,但P3中的IdInfo 和 P1中的IdInfo 不是同一個物件,也就是說 P3中的IdInfo是一個全新的物件,開闢了自己的記憶體地址!
上述程式碼測試如下:
我們現在講程式碼修改如下:
public static void Main() { //建立P1物件 Person p1 = new Person(); p1.Age = 42; p1.Name = "Sam"; p1.IdInfo = new IdInfo("081309207"); //通過淺複製 得到P2物件 Person p2 = p1.ShallowCopy(); //分別輸出 Console.WriteLine("物件P1相關屬性如下"); DisplayValues(p1); p1.Name = "淺複製中,修改了P1的Name屬性,但Name是值型別,所以不會影響P2"; p1.IdInfo.IdNumber = "淺複製中,修改了P1的IdInfo屬性,但IdInfo是引用型別,所以會影響P2 (淺複製中引用型別原始物件和副本指向同一記憶體地址)"; Console.WriteLine("物件P2相關屬性如下"); DisplayValues(p2); Console.Read(); }
在輸出P2之前,我們修改了P1物件的值型別Name 和 引用型別 IdInfo 。
無論是淺複製還是深複製,副本中的值型別都是全新的!
淺複製中原始物件和副本的引用型別指向同一記憶體地址,所以,修改了P1的IdInfo會同時影響P2的IdInfo
輸出如下:
繼續修改程式碼,如下:
public static void Main() { //建立P1物件 Person p1 = new Person(); p1.Age = 42; p1.Name = "Sam"; p1.IdInfo = new IdInfo("081309207"); //現在測試深複製 Person p3 = p1.DeepCopy(); p1.Name = "George"; p1.Age = 39; p1.IdInfo.IdNumber = "081309208"; Console.WriteLine("物件P1相關屬性如下"); DisplayValues(p1); p1.IdInfo.IdNumber = "深複製中,修改了P1的IdInfo屬性,即使IdInfo是引用型別,也不會影響P3 (深複製中引用型別原始物件和副本分別指向不同的記憶體地址)"; Console.WriteLine("物件P3相關屬性如下"); DisplayValues(p3); Console.Read(); }
深複製中原始物件和副本的引用型別指向各自的地址,兩者完全是兩個不同的物件!
因此:修改P1不會影響P3
so,是不是很簡單,是不是很Easy.
深淺複製主要用於當建立一個物件需要消耗過多資源時,可以採取複製的方法提升效率!
大話設計模式的原話是這樣滴:當你New一個物件時,每New一次,都需要執行一個建構函式,如果建構函式的執行時間很長,那麼多次New物件時會大大拉低程式執行效率,因此:一般在初始化資訊不發生變化的前提下,克隆是最好的辦法,這既隱藏了物件的建立細節,又大大提升了效能!
當然,如果每個類都要寫自己的深複製,這豈不是非常非常麻煩,因此,有一個通用的深複製方法,如下:
/// <summary> /// 通用的深複製方法 /// </summary> /// <typeparam name="T"></typeparam> [Serializable] public class BaseClone<T> { public virtual T Clone() { MemoryStream memoryStream = new MemoryStream(); BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(memoryStream,this); memoryStream.Position = 0; return (T)formatter.Deserialize(memoryStream); } }