| 特性 | 結構 | 類別 |
|---|---|---|
| 型別分類 | 值型別 | 參考型別 |
| 記憶體配置 | 堆疊 (或內嵌在物件內) | 受控堆積 |
| 指派語意 | 複製整個實例 (深層複製) | 複製參考 (淺層複製) |
| 繼承 | 不可繼承類別/結構,隱含 sealed;可實作介面 | 單一繼承,可標記 sealed 防繼承 |
| 建構函式 | 無預設無參建構子;可自訂有參建構子 | 有預設無參建構子;可多載 |
| 解構函式 | 不支援 | 支援 (Finalizer) |
| null 值 | 不可為 null (除非用 Nullable<T>) | 可為 null |
| 預設值 | 所有欄位歸零 | null |
// 程式碼
struct Point { public int X, Y; }
class Node { public int X, Y; }
var s = new Point { X = 1, Y = 2 }; // 堆疊配置
var c = new Node { X = 1, Y = 2 }; // 堆積配置,s 直接持有值,c 持有參考
var s2 = s; // 複製值,s2 獨立於 s
var c2 = c; // 複製參考,c2 與 c 指向同一物件
s2.X = 99; // 不影響 s
c2.X = 99; // 影響 c (c.X 也變 99)
✅ 符合所有條件 才建議用 `struct`:
✅ 其餘情況 皆建議用 `class`:
// 唯讀結構:編譯器強制所有欄位 readonly,避免防禦性複製
public readonly struct Vector3
{
public double X { get; }
public double Y { get; }
public double Z { get; }
public Vector3(double x, double y, double z) => (X, Y, Z) = (x, y, z);
}
// ref struct:只能存在於堆疊,不能盒裝、不能作為欄位、不能用於 async/iterator
// 適用於高效能 Span-like 型別
public ref struct SpanWrapper
{
private Span<byte> _span;
public SpanWrapper(Span<byte> span) => _span = span;
}
| 錯誤寫法 | 問題 | 修正 |
|---|---|---|
| 結構欄位未初始化就使用 | 編譯錯誤 CS0165 | 建構子初始化所有欄位,或使用 `default` |
| 結構實作介面並頻繁當作介面傳遞 | 盒裝產生 GC 壓力 | 改用泛型約束 `where T : IInterface` 避免盒裝 |
| 以類別思維設計結構 (可變、大型) | 複製語意導致 Bug、效能差 | 改用 class 或 immutable struct |
結構與類別的選擇本質上是值語意 vs 參考語意、堆疊 vs 堆積、複製 vs 共享的權衡。遵循「預設用類別,符合值型別準則才用結構」原則,並善用 `readonly struct`、`record struct` (C# 10) 等現代特性,能寫出既安全又高效的程式碼。