// 程式碼
int a = 10; // 值型別區域變數
int b = a; // 複製值
b = 20; // a 不受影響
class Ref { public int Val; }
Ref r1 = new Ref { Val = 10 }; // r1 在 Stack,物件在 Heap
Ref r2 = r1; // 複製參考
r2.Val = 20; // r1.Val 也變 20
void ByVal(int x) { x = 20; }
void ByRef(ref int x) { x = 20; }
void ByOut(out int x) { x = 20; }
void ByIn(in int x) { /* x = 20; 編譯錯誤 */ }
int v = 10;
ByVal(v); // v = 10
ByRef(ref v); // v = 20
ByOut(out v); // v = 20
ByIn(in v); // v = 20 (不變)
裝箱與拆箱
// 裝箱:值型別 → object/介面 (Heap 配置、複製資料)
int i = 10;
object o = i; // 隱式裝箱
Console.WriteLine(o); // 10
// 拆箱:object/介面 → 值型別 (型別檢查、複製資料回 Stack)
int j = (int)o; // 顯式拆箱
// int k = (long)o; // InvalidCastException:必須精確型別
// 介面裝箱
IComparable cmp = 10; // int 實作 IComparable → 裝箱
效能影響
裝箱:Heap 配置 + 複製 + GC 壓力
拆箱:型別檢查 + 複製
頻繁裝箱 (如 `List
結構體 設計指南
// ✅ 適合結構體:小型、不可變、值語意、主要儲存資料
public readonly struct Point3D {
public double X { get; }
public double Y { get; }
public double Z { get; }
public Point3D(double x, double y, double z) { X=x; Y=y; Z=z; }
public double Distance => Math.Sqrt(X*X + Y*Y + Z*Z);
}
// ❌ 不適合結構體:大型、可變、需多型、頻繁裝箱、有身分識別
class Entity { /* ... */ }
結構體大小建議
< 16 bytes:ByVal 傳遞極快,推薦
16-64 bytes:考慮 `in` 參數或改 class
> 64 bytes:強烈建議改 class,避免複製開銷
Nullable 值型別 (T? / Nullable<T>)
int? x = null; // 合法
int y = x ?? 0; // Null 聯合運算子
int z = x.GetValueOrDefault(); // 預設值 0
// 底層實作:struct Nullable { bool hasValue; T value; }
// 裝箱時:hasValue=false → null;hasValue=true → 裝箱 value
字串 特殊參考型別
參考型別,但**不可變**
== 多載為值比較
字串驻留:相同字面常數編譯期合併,`string.Intern` 可手動驻留
頻繁修改用 StringBuilder (Heap 可變緩衝區)
效能實測參考 (BenchmarkDotNet)
// 100萬次迴圈
struct Small { public int A, B; } // 8 bytes
struct Large { public int A,B,C,D,E,F,G,H; } // 32 bytes
// ByVal 呼叫
Small s = new(); ByVal(s); // ~5 ms
Large l = new(); ByVal(l); // ~25 ms (複製 4x 資料)
// in 參數
ByIn(in l); // ~1 ms (無複製)
// 結構體 vs 類別建立
new Small(); // ~2 ms (Stack)
new LargeClass(); // ~8 ms (Heap 配置 + GC 追蹤)