1. 程式人生 > WINDOWS開發 >C# 深淺複製 MemberwiseClone

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);

        }
    }