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

C# 類型系統深度解析:值類型、參考類型、裝箱與拆箱

     編輯:關於C語言

前言:統一類型系統的基石

C# 擁有統一類型系統:所有類型 (含基本型別) 皆繼承自 object。這設計讓基本型別 (int、bool 等) 也能擁有方法 (如 ToString()),並能無縫存入集合、當作參數傳遞。本文深入剖析值類型與參考類型的記憶體行為、裝箱/拆箱機制及效能影響。

一、值類型 — 存放在堆疊

值類型變數直接包含資料,指派時發生位元複本。分類如下:

1. 簡單類型 (Simple Types) — 內建型別別名

C# 關鍵字.NET 型別大小範圍 / 精度預設值
sbyteSystem.SByte8 位元-128 ~ 1270
byteSystem.Byte8 位元0 ~ 2550
shortSystem.Int1616 位元-32,768 ~ 32,7670
ushortSystem.UInt1616 位元0 ~ 65,5350
intSystem.Int3232 位元-2,147,483,648 ~ 2,147,483,6470
uintSystem.UInt3232 位元0 ~ 4,294,967,2950
longSystem.Int6464 位元-9.22×10¹⁸ ~ 9.22×10¹⁸0L
ulongSystem.UInt6464 位元0 ~ 1.84×10¹⁹0UL
floatSystem.Single32 位元±1.5×10⁻⁴⁵ ~ ±3.4×10³⁸ (7 位精度)0.0f
doubleSystem.Double64 位元±5.0×10⁻³²⁴ ~ ±1.7×10³⁰⁸ (15-16 位精度)0.0
decimalSystem.Decimal128 位元±1.0×10⁻²⁸ ~ ±7.9×10²⁸ (28-29 位精度)0.0m
boolSystem.Boolean1 位元 (實際 1 byte)true / falsefalse
charSystem.Char16 位元U+0000 ~ U+FFFF (單一 Unicode 字元)'\0'

關鍵差異:C# vs C/C++

  • int 固定 32 位元,不隨平台變化。
  • long 固定 64 位元。
  • bool 嚴格只能是 true/false,不可 隱含轉換自整數 (杜絕 if (x = 5) 這類 Bug)。
  • char 為 UTF-16 代碼單元,非 ASCII。

2. 結構類型 — 自訂值類型

public struct IPAddress
{
    public byte Octet1, Octet2, Octet3, Octet4;
    
    public IPAddress(byte o1, byte o2, byte o3, byte o4)
    {
        Octet1 = o1; Octet2 = o2; Octet3 = o3; Octet4 = o4;
    }
    
    public override string ToString() => $"{Octet1}.{Octet2}.{Octet3}.{Octet4}";
}

適用場景:小型、不可變、邏輯單一的資料結構 (如座標點、顏色、日期時間區間)。避免參考類型的堆積配置與 GC 壓力。

3. 列舉類型 — 命名常數集合

public enum Month : byte // 指定底層型別為 byte 節省空間
{
    January = 1, February = 2, March = 3, April = 4,
    May = 5, June = 6, July = 7, August = 8,
    September = 9, October = 10, November = 11, December = 12
}

預設底層型別為 int,可指定 byte, sbyte, short, ushort, int, uint, long, ulong。

二、參考類型 — 存放在受控堆積

參考類型變數存放「物件參考」(記憶體位址),指派時複製參考,指向同一物件。分類:

  • 類別:class — 單一繼承、支援介面、完整 OO 功能。
  • 介面:interface — 定義契約,可多重實作。
  • 委派:delegate — 類型安全的函式指標,事件基礎。
  • 字串:string — 不可變參考類型,== 比較值而非參考。
  • 陣列:T[] — 階層固定、元素同型別。
  • object:System.Object — 所有類型的共同基類。

三、裝箱與拆箱:值類型與參考類型的橋樑

1. 裝箱 — 隱含轉換

int value = 2024;
object boxed = value; // 裝箱發生

// IL 指令:box System.Int32
// 記憶體動作:
// 1. 在堆積配置新物件 (含型別標頭、同步區塊索引、資料副本)
// 2. 將值型別資料複製進物件
// 3. 回傳物件參考給 boxed 變數

2. 拆箱 — 顯式轉換

object boxed = 2024;
int unboxed = (int)boxed; // 拆箱發生

// IL 指令:unbox.any System.Int32
// 記憶體動作:
// 1. 檢查 boxed 是否為 null
// 2. 驗證物件實際型別是否為 int (否則拋 InvalidCastException)
// 3. 取得物件內資料位址,複製資料到堆疊變數 unboxed

3. 記憶體獨立性驗證

int original = 100;
object boxed = original;
boxed = 200; // 修改 boxed 參考指向的新物件 (或重新裝箱)
Console.WriteLine($"original={original}, boxed={boxed}"); // 輸出: 100, 200

裝箱建立的是資料副本,兩者生命週期與內容完全獨立。

四、效能影響與最佳實踐

操作成本建議
裝箱堆積配置 + 複製 + GC 壓力避免在熱路徑 (迴圈、高頻呼叫) 裝箱
拆箱型別檢查 + 複製同左
非泛型集合每次存取皆裝箱/拆箱務必使用泛型集合 (List<T>, Dictionary<TKey,TValue>)
介面呼叫值類型裝箱若頻繁呼叫,考慮讓結構實作介面並透過泛型約束 where T : IInterface 避免裝箱

常見裝箱陷阱

// 1. String.Format / Console.WriteLine 參數陣列
Console.WriteLine("Value: {0}", 42); // 42 被裝箱

// 2. 非泛型集合
ArrayList list = new ArrayList();
list.Add(42); // 裝箱
int x = (int)list[0]; // 拆箱

// 3. 介面呼叫
IComparable cmp = 42; // 裝箱
cmp.CompareTo(10);    // 虛擬呼叫

// 改善:使用泛型
List list = new List();
list.Add(42); // 無裝箱
int x = list[0]; // 直接存取

五、進階主題:readonly struct 與 ref struct (C# 7.2+)

// 唯讀結構:防止防禦性複製,提升效能
public readonly struct Vector3
{
    public double X, Y, Z;
    public Vector3(double x, double y, double z) { X=x; Y=y; Z=z; }
    public double Length => Math.Sqrt(X*X + Y*Y + Z*Z); // 唯讀屬性
}

// ref struct:只能存在於堆疊,不可裝箱、不可為類別欄位、不可用於 async/iterator
public ref struct SpanWrapper
{
    private Span _span;
    public SpanWrapper(Span span) => _span = span;
}

總結

  1. 值類型:堆疊配置、語意複製、無繼承、適合小型資料。
  2. 參考類型:堆積配置、語意共享、支援繼承/多型、適合複雜物件。
  3. 裝箱/拆箱:橋接兩世界,但有效能代價,熱路徑必須避免。
  4. 泛型:消除裝箱的核心工具,現代 C# 開發的基石。
  5. 新特性:readonly struct、ref struct 進一步釋放值類型潛能。

深入理解類型系統,是寫出高效能、低 GC 壓力、無驚喜 Bug 的 C# 程式碼的前提。

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