結構 (struct) 與類別 (class) 同樣支援屬性、方法、建構函式、介面實作,但本質差異巨大:
| 特性 | 類別 (參考型別) | 結構 (值型別) |
|---|---|---|
| 記憶體配置 | Managed Heap | Stack (或內嵌於父物件) |
| 變數指派 | 複製參考 (雙方指向同一物件) | 複製實體值 (獨立副本) |
| 方法傳遞 | 傳參考 (預設) | 傳值 (建立副本) |
| 繼承 | 單一繼承,可為基底類別 | 不可繼承其他結構/類別,隱含繼承 System.ValueType |
| 預設建構函式 | 隱含無參建構函式 | 隱含無參建構函式 (將所有欄位歸零),不可自訂無參建構函式 |
| Null 值 | 可為 null | 不可為 null (除非包裝於 Nullable<T>) |
結構實體建立於堆疊,配置/釋放極快 (指標遞增/遞減),無 GC 壓力。適合 小型、不可變、生命週期短、大量建立 的資料載體 (如座標點、複數、日期時間)。但頻繁傳遞大型結構會導致堆疊複製開銷,此時應改用類別或 ref/in 傳遞。
new Struct() 呼叫的是預設建構函式 (全欄位歸零)。public int X { get; set; } = 1; (自動屬性),但仍不支援欄位初始化器。new 宣告變數時,欄位維持未指派狀態,必須在使用前逐一賦值。
public interface IImage { void Paint(); }
public struct Picture : IImage
{
public void Paint() { /* 繪圖邏輯 */ }
private int _x, _y, _z;
}
結構實作介面時,若透過介面變數存取,會發生 裝箱,產生堆積配置,喪失值型別效能優勢。避免裝箱技巧:使用泛型約束 where T : IImage 或 C# 7.2+ readonly struct 配合 in 參數。
StructLayout 與 FieldOffset預設 LayoutKind.Sequential 依宣告順序排列。透過 [StructLayout(LayoutKind.Explicit)] 可精確控制每個欄位位移,實現 聯集 效果:
[StructLayout(LayoutKind.Explicit)]
public struct TestUnion
{
[FieldOffset(0)] public int IntValue;
[FieldOffset(0)] public double DoubleValue;
[FieldOffset(0)] public char CharValue;
[FieldOffset(0)] public byte ByteValue;
}
// 所有欄位共用相同 8 位元組記憶體 (以最大者為準)
另一範例:手工拆解 long 為兩個 int:
[StructLayout(LayoutKind.Explicit)]
public struct TestExplicit
{
[FieldOffset(0)] public long LongValue;
[FieldOffset(0)] public int LowInt;
[FieldOffset(4)] public int HighInt;
[FieldOffset(8)] public double DoubleValue; // 覆蓋前 8 bytes
}
此技術常用於 P/Invoke 呼叫原生 API、高效能序列化、硬體暫存器映射。
readonly) vs 常數 (const)const:編譯期常數,必須在宣告時以常數運算式初始化,隱含 static,僅限基本型別、string、enum 或 null 參考。readonly:執行期常數,可在宣告或建構函式中賦值,支援任意型別 (含陣列、物件實例),可為 static 或實例欄位。跨組件版本控制陷阱:const 值會在編譯期內嵌至呼叫端 IL,修改後需重編譯所有參考組件;static readonly 則在執行期讀取,僅需重編譯定義組件。
結構是 C# 高效能程式設計的利器。掌握值型別語意、記憶體佈局控制、避免裝箱技巧,能編寫出既安全又極速的系統級程式碼。記住:小而不可變 是結構的最佳應用場景。