首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

.NET 深度學習筆記 (4):深拷貝與淺拷貝完整解析——機制、實作與效能考量

     編輯:關於.NET

深拷貝與淺拷貝是 C# 進階面試必考題,也是設計高效能、正確資料結構的關鍵知識。本文系統整理概念定義、實作機制、程式碼範例與執行結果分析,建立完整認知框架。

核心概念定義

類型定義值型別處理參考型別處理記憶體關係
淺拷貝
(Shallow Copy)
僅複製物件欄位值,參考型別欄位複製「參考位址」逐位元複製 (Bitwise copy)複製參考,新舊物件指向同一個實體物件共享參考型別資料
深拷貝
(Deep Copy)
完全建立新物件,包含遞迴複製所有參考型別欄位指向的實體逐位元複製遞迴建立新的實體物件並複製內容完全獨立,互不影響

.NET 實作機制對應

值型別:淺/深拷貝無差異

int[] arr1 = { 1, 2, 3 };
int[] arr2 = arr1;              // 參考賦值 (非拷貝)
int[] arr3 = (int[])arr1.Clone(); // 淺拷貝 = 深拷貝 (值型別陣列)
arr3[0] = 99;  // arr1[0] 仍為 1

值型別陣列 Clone()、CopyTo()、Array.Copy() 皆產生獨立副本。

參考型別:關鍵差異在 MemberwiseClone vs 自訂 Clone

方法類別行為適用場景
MemberwiseClone()Object建立淺副本:值型別複製、參考型別複製參考淺拷貝基礎實作
ICloneable.Clone()介面語意由實作者定義,通常實作深拷貝公開深拷貝契約

完整程式碼實作與驗證

1. 值型別陣列淺拷貝多種方法

public class ValueTypeCopy
{
    public void MethodShallowCopyDirectly()
    {
        int[] arr1 = { 0, 1, 2, 3 };
        int[] arr2 = arr1;  // 參考賦值,非拷貝
        arr2[0] = 8;
        // arr1[0] 變為 8 → 同一陣列實體
    }

    public void MethodShallowCopyArrayClone()
    {
        int[] arr1 = { 0, 1, 2, 3 };
        int[] arr2 = (int[])arr1.Clone();  // 新陣列,值複製
        arr2[0] = 8;  // arr1[0] 仍為 0
    }
    
    // CopyTo、Array.Copy 同理產生獨立值型別陣列
}

2. 參考型別陣列 (string[]) 淺 vs 深拷貝

// 淺拷貝:複製參考
string[] src = { "A", "B", "C" };
string[] shallow = src;  // 或 src.Clone()
shallow[0] = "X";  // src[0] 也變 "X" → 共享字串物件 (不可變特性掩蓋此問題)

// 真正驗證需用可變參考型別
class Data { public int Value; }
Data[] src = { new Data{Value=1}, new Data{Value=2} };
Data[] shallow = (Data[])src.Clone();
shallow[0].Value = 99;  // src[0].Value 也變 99 → 共享 Data 實體

// 深拷貝:逐項建立新實體
Data[] deep = new Data[src.Length];
for (int i = 0; i < src.Length; i++)
    deep[i] = new Data { Value = src[i].Value };
deep[0].Value = 88;  // src[0].Value 仍為 1 → 完全獨立

3. 自訂類別實作 ICloneable:深 vs 淺

// 深拷貝實作
public class CarDeepClone : ICloneable
{
    public string Name { get; set; }
    public int Price { get; set; }
    public Engine Engine { get; set; }  // 參考型別欄位

    public object Clone()
    {
        return new CarDeepClone 
        { 
            Name = this.Name, 
            Price = this.Price,
            Engine = this.Engine != null ? (Engine)this.Engine.Clone() : null  // 遞迴深拷貝
        };
    }
}

// 淺拷貝實作
public class CarShallowClone : ICloneable
{
    public string Name { get; set; }
    public int Price { get; set; }
    public Engine Engine { get; set; }

    public object Clone()
    {
        return this.MemberwiseClone();  // 僅複製參考
    }
}

4. 客戶端驗證測試

// 深拷貝測試
var car1 = new CarDeepClone { Name = "Benz", Price = 700, Engine = new Engine { HP = 300 } };
var car2 = (CarDeepClone)car1.Clone();
car2.Name = "BMW";
car2.Engine.HP = 400;
// car1.Name 仍為 "Benz", car1.Engine.HP 仍為 300 → 完全獨立

// 淺拷貝測試
var car3 = new CarShallowClone { Name = "Audi", Price = 500, Engine = new Engine { HP = 250 } };
var car4 = (CarShallowClone)car3.Clone();
car4.Name = "VW";
car4.Engine.HP = 350;
// car3.Name 仍為 "Audi" (string 不可變), 但 car3.Engine.HP 變為 350 → 共享 Engine 實體

執行結果關鍵發現

深淺拷貝執行結果圖示

  • 值型別陣列:Clone()、CopyTo()、Array.Copy() 皆為深拷貝效果
  • 參考型別陣列 Clone():淺拷貝,元素參考共享
  • 自訂類別 MemberwiseClone():淺拷貝,參考型別欄位共享實體
  • 自訂 Clone() 配合 new 建構:深拷貝,完全獨立

進階議題與最佳實踐

序列化深拷貝通用實作

public static T DeepClone<T>(T obj) where T : class
{
    using (var ms = new MemoryStream())
    {
        var formatter = new BinaryFormatter();
        formatter.Serialize(ms, obj);
        ms.Position = 0;
        return (T)formatter.Deserialize(ms);
    }
}
// 需標記 [Serializable],效能較差但零程式碼維護

不可變物件模式避免拷貝困擾

  • 所有屬性 readonly / init (C# 9+)
  • 修改需建立新實體 (with 運算子 / 方法回傳新物件)
  • 天然執行緒安全,無拷貝需求

效能比較建議

方法效能適用性
MemberwiseClone + 手動深拷貝參考欄位★★★★★高效能、固定結構
序列化/反序列化★★☆☆☆複雜物件圖、跨進程
JSON 循環序列化★★★☆☆POCO、無循環參考
Expression Tree 編譯複製★★★★☆高頻呼叫、動態型別

掌握深淺拷貝本質,依據物件圖複雜度、效能需求、維護成本選擇適當策略,是 .NET 開發者進階的關鍵里程碑。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved