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

C# 結構體 完整指南:值型別語意、建構函式限制、記憶體模型與最佳實踐

     編輯:關於C語言

結構體 vs 類別:核心差異

特性struct (值型別)class (參考型別)
記憶體配置Stack (區域變數) / Inline (陣列/欄位)Heap (Managed Heap)
指派行為複製整個實例資料複製參考 (指向同一物件)
預設值所有欄位歸零 (0/null/false)null
繼承不可繼承其他 struct/class,隱性繼承 ValueType單一繼承,可繼承介面
介面實作可實作介面可實作介面
解構函式不可宣告可宣告 (Finalizer)
建構函式不可有無參數建構函式 (C# 10 前)可有無參數建構函式
欄位初始化宣告時不可初始化 (除非 const/static)可直接初始化
null 能力需包裝為 Nullable<T> (T?)天然可為 null

基本宣告與使用

public struct Point
{
    public int X;
    public int Y;
    
    // C# 10+ 允許無參數建構函式,但仍會被忽略 (new Point() 仍歸零)
    public Point(int x, int y) { X = x; Y = y; }
    
    public override string ToString() => $"({X}, {Y})";
}

// 使用
Point p1 = new Point(3, 4);  // 呼叫建構函式
Point p2 = new Point();      // X=0, Y=0 (歸零)
Point p3;                    // 未初始化,使用前必須指派
p3.X = 10; p3.Y = 20;        // 現在可用

// 複製語意:p4 獲得 p1 的獨立副本
Point p4 = p1; 
p4.X = 100; // p1.X 仍為 3,互不影響

進階特性:實作介面、運算子多載、巢狀型別

public struct Vector2 : IEquatable, IFormattable
{
    public float X, Y;
    
    public Vector2(float x, float y) { X = x; Y = y; }
    
    // 運算子多載
    public static Vector2 operator +(Vector2 a, Vector2 b) 
        => new Vector2(a.X + b.X, a.Y + b.Y);
    
    public static Vector2 operator *(Vector2 v, float s) 
        => new Vector2(v.X * s, v.Y * s);
    
    // 介面實作
    public bool Equals(Vector2 other) => X == other.X && Y == other.Y;
    public override bool Equals(object obj) => obj is Vector2 v && Equals(v);
    public override int GetHashCode() => HashCode.Combine(X, Y);
    
    public string ToString(string format, IFormatProvider provider)
        => $"({X.ToString(format, provider)}, {Y.ToString(format, provider)})";
}

記憶體模型深度解析

Stack 配置 (區域變數)

void Method()
{
    Point p = new Point(1, 2); // 直接在 Stack 配置 8 bytes (兩個 int)
    // 無 GC 壓力,方法結束自動釋放
}

Inline 配置 (陣列/類別欄位)

Point[] points = new Point[1000]; 
// 連續記憶體 1000 * 8 bytes = 8KB,單一陣列物件於 Heap
// 優點:Cache 友好、無額外物件標頭、GC 掃描快

class Container 
{
    Point _center; // 直接內嵌於 Container 物件中,非獨立 Heap 物件
}

Boxing / Unboxing 成本

Point p = new Point(1, 2);
object boxed = p;        // Boxing: Heap 配置物件、複製值、加上標頭
Point unboxed = (Point)boxed; // Unboxing: 型別檢查、複製值回 Stack

// 避免裝箱技巧:
// 1. 使用泛型 List 而非 ArrayList
// 2. 介面約束 where T : struct 避免裝箱
// 3. 實作介面時小心:介面變數持有 struct 會裝箱
IEquatable eq = p; // 裝箱發生!

可為 Null 的值型別

Point? nullable = null; // 實際為 Nullable
nullable = new Point(5, 6);
if (nullable.HasValue) Console.WriteLine(nullable.Value);
Console.WriteLine(nullable.GetValueOrDefault()); // 安全取值

// C# 7+ 模式比對
if (nullable is Point pt) Console.WriteLine(pt);

何時選擇 struct?

✅ 適用:

  • 小型資料結構 (16 bytes 以下建議)
  • 主要儲存資料、行為單純
  • 大量實例 (陣列、集合) 需記憶體效率
  • 不可變資料結構 (readonly struct C# 7.2+)
  • 單位量值、座標、矩陣、色彩、日期時間元件

❌ 不適用:

  • 需頻繁傳遞參考、共享狀態
  • 複雜物件圖、深層繼承
  • 需 Finalizer 清理非受控資源
  • 大型結構 (複製成本高)

現代 C# 增強 (C# 7.2+)

// 唯讀結構:編譯器強制所有欄位 readonly,方法隱性 this 為 in 參數
public readonly struct ImmutablePoint
{
    public int X { get; }
    public int Y { get; }
    public ImmutablePoint(int x, int y) { X = x; Y = y; }
}

// ref struct:只能存在於 Stack,不可作為類別欄位、陣列元素、Boxing
public ref struct SpanWrapper
{
    public Span Data;
    // 只能用於同步方法參數、區域變數
}
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved