值型別 vs 參考型別:記憶體怎麼運作?
核心差異
Stack(堆疊) Heap(堆積)
├── int a = 5 ├── string s = "hello" → [h,e,l,l,o]
├── bool b = true ├── int[] arr = {1,2,3} → [1,2,3]
├── char c = 'A' ├── User u = new() → {Name="小明", Age=20}
└── 快速、自動清理 └── 較慢、需要 GC 回收
| 值型別(Value Type) | 參考型別(Reference Type) | |
|---|---|---|
| 存在哪 | Stack | Heap(Stack 放指標) |
| 複製行為 | 複製整個值 | 複製指標(共享資料) |
| 例子 | int, bool, char, struct, enum | string, class, array, interface |
| 預設值 | 0, false, '\0' | null |
複製行為的差異(面試必考)
// 值型別:複製值
int a = 5;
int b = a; // b 是獨立副本
b = 10;
Console.WriteLine(a); // → 5(不受影響)
// 參考型別:複製指標
int[] arr1 = { 1, 2, 3 };
int[] arr2 = arr1; // arr2 指向同一個陣列
arr2[0] = 99;
Console.WriteLine(arr1[0]); // → 99(被改了!)
Boxing / Unboxing
int x = 42;
object obj = x; // Boxing:值型別 → 堆積(包裝成 object)
int y = (int)obj; // Unboxing:堆積 → 值型別(拆包)
Boxing 有效能成本:每次都要在 Heap 配置記憶體。大量 Boxing 會造成 GC 壓力。
string 的特殊性
string a = "hello";
string b = a;
b = "world";
Console.WriteLine(a); // → "hello"(沒被改)
string 是參考型別,但行為像值型別。因為 string 是不可變的(immutable)。 每次 "修改" string,其實是建立新的 string 物件。
// ❌ 效能差:每次 += 都建立新 string
string result = "";
for (int i = 0; i < 10000; i++) {
result += i.ToString(); // 10000 次記憶體配置!
}
// ✅ 用 StringBuilder
var sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.Append(i);
}
string result = sb.ToString(); // 一次配置
struct vs class
// struct(值型別):小型、不可變的資料用
public struct Point { public int X; public int Y; }
// class(參考型別):有行為、需要繼承、較大的物件用
public class User { public string Name; public int Age; }
| 用 struct | 用 class |
|---|---|
| 小於 16 bytes | 大型物件 |
| 不需要繼承 | 需要繼承 |
| 不可變(immutable) | 可變(mutable) |
| 頻繁建立和銷毀 | 長生命週期 |