深拷貝與淺拷貝是理解 C# 內存模型的關鍵。本文通過值類型、引用類型、字符串、數組等實例,系統闡述拷貝行為差異與應對策略。
| 類型 | 拷貝行為 | 典型例子 |
|---|---|---|
| 值類型 | 深拷貝:逐位複製,互不影響 | int、double、struct、enum |
| 引用類型 | 淺拷貝:複製引用,共享堆上對象 | class、string、array、delegate |
int source = int.MaxValue; // 2,147,483,647
int dest = source; // 深拷貝,複製數值
dest = 1024; // dest 變化
source = 2048; // source 變化
// 兩者完全獨立,互不干擾
struct Point { public int X, Y; public Point(int x, int y) { X=x; Y=y; } }
Point s = new Point(10, 20);
Point d = s; // 深拷貝,複製所有字段
d.X = 20; // d.X=20, s.X=10 互不影響
class Point { public int X, Y; public Point(int x, int y) { X=x; Y=y; } }
Point s = new Point(10, 20);
Point d = s; // 淺拷貝,複製引用(地址)
d.X = 20; // s.X 也變為 20!指向同一對象
棧 堆
┌─────────┐ ┌──────────────┐
│ s ─────┼────────►│ Point {10,20}│
└─────────┘ └──────────────┘
▲ ▲
│ │
┌─────────┐ (同一對象)
│ d ─────┘
└─────────┘
Point d = new Point(s.X, s.Y); // 或
Point d = new Point();
d.X = s.X; d.Y = s.Y;
class Point : ICloneable {
public int X, Y;
public object Clone() => new Point(X, Y); // 淺層字段深拷貝
}
// BinaryFormatter (舊)、JSON.NET、MessagePack 等
string json = JsonConvert.SerializeObject(obj);
T copy = JsonConvert.DeserializeObject(json);
| 類型 | 行為 | 原因 |
|---|---|---|
| string | 表現為值類型 | 不可變,任何修改都產生新對象 |
| array | 淺拷貝 | 複製引用,元素若為引用類型則共享 |
| delegate | 淺拷貝 | 複製委託引用,目標方法同一 |
string s = "hello";
string d = s; // 複製引用
d = "world"; // d 指向新字符串,s 不變
// 看似深拷貝,實則是不可變性導致
int[] src = {1, 2, 3};
int[] dst = new int[src.Length];
Array.Copy(src, dst, src.Length); // 或 src.CopyTo(dst, 0)
// 值類型元素:深拷貝
// 引用類型元素:仍是淺拷貝(需逐元素 Clone)
new 創建null 是引用類型特徵ICloneable、複製構造函數、或序列化list.ToArray()、new List(list) 均為淺拷貝record 自帶值語義與 with 表達式深/淺拷貝本質是「數據所有權」與「共享可變狀態」的權衡。值類型通過複製消除共享,引用類型通過共享節省內存但引入耦合。理解這一點,配合 readonly、record、不可變模式等現代 C# 特性,能寫出更健壯、可維護的代碼。