C#深度復制和淺度復制


C#深度復制和淺度復制

復制一個值變量很簡單,新建一個變量然后將原來的變量賦值過去就行,但是復制一個引用變量這種方法是不行的,如果不明白為什么可以先看看這篇解釋
引用類型變量和值類型變量在賦值時的不同

如果要復制一個引用類型的變量,比如說類,需要在類定義中繼承ICloneable接口,並實現Clone方法,這是一個固定格式,下面看一個例子

 public class Test:ICloneable
 {
     public int Val;
     public object Clone() => MemberwiseClone();
 }

定義了一個Test類,繼承ICloneable接口,實現Clone方法,實現Clone方法的格式是固定的,這里使用了簡化寫法,public object Clone() => MemberwiseClone();等於

public object Clone()
{
    return MemberwiseClone();
}

這是C#提供的方法,按照這么寫就對了,此時如果要復制一個Test類的引用變量,就可以這么寫

Test t1 = new Test();
Test t2 = (Test)t1.Clone();

有一個引用類型變量t1,調用t1.Clone()會在堆中重新開辟一個內存空間,並且復制t1堆空間的值,然后會返回這個新空間的內存地址,因為Clone()方法返回類型是object,所以強制類型轉換為Test,然后賦給Test類型的變量t2
Clone方法后的內存變化

這個過程,便是C#中的淺度復制(ShallowCopy),也有稱為影子復制的
這個復制會存在一些問題,那就是一個引用類型中存在引用類型字段時,引用類型字段並不會也復制一份

public class Test:ICloneable
{
    public int Val;
    public Test2 Z = new Test2();
    public object Clone()
    {
        return MemberwiseClone();
    }
}
public class Test2
{
    public int D;
}

這里定義了兩個類,其中Test類中包含了一個Test2類型的引用類型變量Z,我們先看內存中怎樣表示的Test類型引用類型變量t1在內存中的情況
從這張圖我們可以看到,當我們使用Clone對引用類型進行Clone時,只會復制堆空間的值,如果堆空間中有引用類型,在復制時就只單純的復制了引用類型的堆空間地址,這樣的后果就是雖然Clone得到了兩個堆空間對象,但是堆空間對象中的字段卻指向了同一個另外的空間地址,在某些情況下就會出現問題
所以
淺度復制:解決了直接使用賦值運算符時變量指向了同一個堆空間的問題(即Test t2 = t1的問題),但是只解決了一層,對於包含在內的堆空間中的成員的引用沒有進一步解決(即t1.Z和t2.Z指向了同一個堆空間地址)

為了解決上述問題,就需要使用深度復制,深度復制的基本思路,就是在Clone方法中創建一個新的對象並使其內容與現有內容保持一致(其實是廢話),我們甚至可以不用繼承ICloneable,但是建議繼承並使用Clone方法,這樣可以保持方法名的一致性
至於怎么實現,可以自行思考(主要是我也在思考中)

所以這篇文章的主要內容是講:C#提供的return MemberwiseClone();只是淺度復制,不會為引用成員創建新空間並將引用成員空間的值復制過去,只會復制引用成員的空間地址,需要注意


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM