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

C# 結構體 vs 類別完整對比:值型別/參考型別、記憶體配置、繼承、效能、選擇指南

     編輯:關於C語言

前言

結構體與類別是 C# 建立自訂型別的兩大基石。雖然語法相似,但語意、記憶體行為、效能特徵截然不同。本文從 CLR 層面深度解析,提供實務選擇決策樹。

核心差異總覽

特性結構體類別
型別分類值型別參考型別
記憶體配置堆疊 或 內嵌於父物件Managed Heap (GC 管理)
變數賦值複製整個實例 (深拷貝)複製參考 (淺拷貝)
預設值所有欄位為 0/null/falsenull
繼承不可繼承類別/結構體,隱式 sealed單一繼承,可標記 sealed
介面實作✅ 可實作介面✅ 可實作介面
建構函式無預設無參建構函式,不可定義無參 ctor有預設無參 ctor,可定義任意 ctor
解構函式❌ 不支援✅ Finalizer (~ClassName)
欄位初始化宣告時不可初始化可宣告時初始化
protected❌ 不支援 (不可繼承)✅ 支援
null 可指派需 Nullable<T> 或 T?原生支援
裝箱/拆箱轉為 object/介面時發生無 (本身即參考)
傳遞給方法預設傳值 (複製),可用 ref/in/out傳參考 (複製參考)

記憶體配置圖解

類別實例:
Stack:                    Heap:
myClass ──────┐           ┌─────────────────┐
              └──────────►│ Object Header   │
                          │ Sync Block Index│
                          ├─────────────────┤
                          │ Field1 (int)    │
                          │ Field2 (string) │──► "Hello" (另一堆疊物件)
                          └─────────────────┘

結構體實例 (區域變數):
Stack:
myStruct ┌─────────────────┐
         │ Field1 (int)    │
         │ Field2 (string) │──► "Hello" (字串仍在堆疊)
         └─────────────────┘

結構體陣列:
Stack:                    Heap:
arr ──────┐               ┌─────────────────┐
          └──────────────►│ Array Header    │
                          ├─────────────────┤
                          │ [0] Field1      │
                          │ [0] Field2      │──► "Hello"
                          │ [1] Field1      │
                          │ [1] Field2      │──► "World"
                          └─────────────────┘

代碼行為驗證


public struct PointStruct
{
    public int X, Y;
    public override string ToString() => $"({X},{Y})";
}

public class PointClass
{
    public int X, Y;
    public override string ToString() => $"({X},{Y})";
}

public static void Demo()
{
    // 1. 賦值行為
    PointStruct s1 = new PointStruct { X = 1, Y = 2 };
    PointStruct s2 = s1; // 複製值
    s2.X = 10;
    Console.WriteLine($"s1: {s1}, s2: {s2}"); // s1: (1,2), s2: (10,2) - 獨立

    PointClass c1 = new PointClass { X = 1, Y = 2 };
    PointClass c2 = c1; // 複製參考
    c2.X = 10;
    Console.WriteLine($"c1: {c1}, c2: {c2}"); // c1: (10,2), c2: (10,2) - 共享

    // 2. 方法傳遞
    ModifyStruct(s1); // 傳值,複製
    Console.WriteLine($"After ModifyStruct: {s1}"); // (1,2) 不變
    
    ModifyClass(c1); // 傳參考
    Console.WriteLine($"After ModifyClass: {c1}"); // (10,2) 變了
}

static void ModifyStruct(PointStruct p) { p.X = 99; }
static void ModifyClass(PointClass p) { p.X = 99; }

效能基準測試


| 操作                    | 結構體 (16 bytes) | 類別 (16 bytes + 參考) |
|------------------------|-------------------|------------------------|
| 實例化 (100萬次)       | ~5 ms             | ~15 ms (含 GC 壓力)    |
| 陣列配置 (100萬)       | 連續記憶體 16MB   | 陣列 8MB + 物件 16MB   |
| 陣列遍歷存取           | 極快 (CPU Cache 友善) | 較慢 (指標追蹤、Cache Miss) |
| 傳遞給方法 (by value)  | 複製 16 bytes     | 複製 8 bytes 參考      |
| 裝箱/拆箱              | 有開銷            | 無                     |
| GC 掃描壓力            | 無 (非堆疊)       | 有                     |

何時使用結構體

  • ✅ 小型、不可變、值語意:座標、顏色、日期時間、錢幣、向量、矩陣
  • ✅ 大量陣列/集合:Point[]、Vector3[] 連續記憶體、Cache 友善
  • ✅ 高頻配置/釋放:遊戲粒子、物理模擬、即時運算,減輕 GC
  • ✅ 原生互操作:struct 對應 C/C++ 結構體、Win32 API 結構
  • ✅ 泛型約束:where T : struct、UnmanagedType

何時使用類別

  • ✅ 實體身分識別:用戶、訂單、產品、實體
  • ✅ 多態/繼承:領域模型、策略模式、插件架構
  • ✅ 大型物件:> 16-24 bytes,傳值複製成本高
  • ✅ 需為 null:選擇性資料、導航屬性
  • ✅ 事件/委派來源:需訂閱生命週期
  • ✅ 最終化邏輯:非託管資源包裝

現代 C# 新特性影響選擇

特性影響
readonly struct (C# 7.2)強制不可變,避免防禦性複製,編譯器優化
ref struct (C# 7.2)僅能棧上配置 (Span、Stackalloc 專用),不可裝箱、不可為欄位
record struct (C# 10)值型別記錄,自動實作 Equality、ToString、with 運算式
record class (C# 9)參考型別記錄,不可變預設、值語意相等
in 參數 (C# 7.2)唯讀傳參考,避免結構體複製,配合 readonly struct 最佳
with 運算式record 專用,非破壞性更新

決策樹:我該用 struct 還是 class?

開始
  │
  ├─ 需要繼承/多態? ──是──► class
  │
  ├─ 需要為 null? ──是──► class (或 T?)
  │
  ├─ 邏輯上是「值」而非「實體」? ──否──► class
  │       (如: 金額、座標、日期、顏色)
  │
  ├─ 實例大小 > 24 bytes? ──是──► class
  │
  ├─ 大量陣列/集合、高頻配置? ──是──► struct (或 readonly struct)
  │
  ├─ 需要實作介面? ──是──► 兩者皆可 (struct 裝箱有開銷)
  │
  └─ 預設選擇 ──► class (較安全、語意明確)

常見陷阱

  1. 可變結構體:List.Count 取得複製品,修改不影響原列表 → readonly struct
  2. 迴圈中修改結構體:foreach (var p in list) p.X = 1; 編譯錯誤 (唯讀) → 用 for 索引
  3. 介面裝箱:IEquatable 透過介面呼叫會裝箱 → 實作介面時考慮 readonly struct
  4. 大型結構體傳值:每次呼叫複製幾百 bytes → 改 in 參數或改 class

總結

結構體 = 值語意、堆疊/內嵌配置、複製語意、無繼承、適合小型高頻資料。類別 = 參考語意、堆疊配置、共享參考、支援繼承/多態、適合實體身分。現代 C# 以 class 為預設,readonly struct/record struct 解決傳統結構體痛點。理解記憶體模型,才能寫出高效能、正確的代碼。

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