博客 / 詳情

返回

C#.NET struct 全解析:什麼時候該用值類型?

簡介

struct 是 值類型(Value Type),用於封裝一組相關的數據。
與類(class)相比,結構體通常更輕量,適用於小型、短生命週期的對象。

⚡ 關鍵特點:

  • 存儲在 棧(stack)上(也可能嵌套在堆中,但本質仍是值類型)。
  • 按值傳遞(賦值/參數傳遞時會複製整個結構)。
  • 無需垃圾回收(GC),生命週期由作用域決定。
  • 可包含字段、屬性、方法、構造函數、運算符重載等。

基本語法

public struct Point
{
    public int X;
    public int Y;

    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }

    public void Move(int dx, int dy)
    {
        X += dx;
        Y += dy;
    }
}

使用:

Point p1 = new Point(3, 4);
p1.Move(1, 2);
Console.WriteLine($"{p1.X}, {p1.Y}"); // 4, 6

內存模型與分配

分配場景 存儲位置 説明
局部變量 最典型場景,生命週期由方法作用域決定
作為類字段 包含在類對象內部,但值本身在堆內存中
數組元素 數組本身在堆上,每個元素緊密排列

即使 struct 位於堆中(例如類字段、數組元素),它仍然是值類型,操作時是按值複製。

與 class 的區別

特性 class(引用類型) struct(值類型)
內存分配 堆(GC 管理) 棧/堆中嵌入
傳遞方式 按引用傳遞(複製引用) 按值傳遞(複製整個對象)
默認構造函數 可定義無參/有參 系統提供無參構造,用户不能定義
繼承 支持繼承/虛方法 不能繼承,只能實現接口
空值(null) 可以為 null 不可為 null(除非 Nullable<T>
裝箱/拆箱 不涉及 賦值給 object 時會裝箱
性能 分配慢(GC) 分配快,生命週期短時更高效

構造函數與初始化

默認構造函數

  • 所有結構體都有一個隱式的無參構造函數,將所有字段初始化為 默認值(如 0null)。
  • 用户不能顯式定義無參構造函數(C# 10 開始支持,但有限制)。

自定義構造函數

可以定義有參構造函數,但必須初始化所有字段。

public Point(int x, int y)
{
    X = x;          // 必須全部賦值
    Y = y;
}

結構體的成員

結構體可以包含:

  • 字段(Field
  • 屬性(Property
  • 方法(Method
  • 事件(Event
  • 運算符重載
  • 靜態成員
  • 構造函數(但無參構造受限)

不支持:

  • 繼承其他類/結構體(只能實現接口)。
  • 明確的析構函數(~Destructor)。

值類型的特性

按值傳遞

結構體賦值時會複製整個值:

Point p1 = new Point(1, 2);
Point p2 = p1;   // 複製
p2.X = 99;
Console.WriteLine(p1.X); // 1,不受 p2 修改影響

裝箱/拆箱

賦值給 object 或接口時,會將結構體複製到堆中:

Point p = new Point(1, 2);
object obj = p;      // 裝箱:複製到堆
Point p2 = (Point)obj; // 拆箱:從堆複製回棧

裝箱/拆箱會帶來性能開銷,應儘量避免頻繁發生。

不可變結構體(Immutable struct)

為了避免複製帶來的副作用,結構體推薦設計為不可變:

public readonly struct Point
{
    public int X { get; }
    public int Y { get; }
    public Point(int x, int y) => (X, Y) = (x, y);
}

實現接口

public interface IDrawable
{
    void Draw();
}

public struct Circle : IDrawable
{
    public double Radius { get; }
    public Point Center { get; }
    
    public Circle(double radius, Point center)
    {
        Radius = radius;
        Center = center;
    }
    
    public void Draw()
    {
        Console.WriteLine($"Drawing circle at {Center} with radius {Radius}");
    }
    
    public double Area => Math.PI * Radius * Radius;
}
struct Rectangle : IEquatable<Rectangle>
{
    public int Width, Height;

    public bool Equals(Rectangle other)
    {
        return Width == other.Width && Height == other.Height;
    }

    public override bool Equals(object obj) => obj is Rectangle other && Equals(other);
    public override int GetHashCode() => HashCode.Combine(Width, Height);
}

var r1 = new Rectangle { Width = 10, Height = 20 };
var r2 = new Rectangle { Width = 10, Height = 20 };
Console.WriteLine(r1.Equals(r2)); // 輸出: True

運算符重載示例

struct Point
{
    public int X, Y;

    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }

    public static Point operator +(Point a, Point b)
    {
        return new Point(a.X + b.X, a.Y + b.Y);
    }

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

var p1 = new Point(1, 2);
var p2 = new Point(3, 4);
var sum = p1 + p2;
Console.WriteLine(sum); // 輸出: (4, 6)

結構體的新特性

readonly struct

  • 表示完全不可變。
  • 編譯器強制所有字段為 readonly
  • 防止無意修改。
public readonly struct Vector
{
    public readonly double X;
    public readonly double Y;
}

ref struct

  • 只能在棧上分配。
  • 用於高性能場景,如 Span<T>

record struct

值類型的 記錄類型,支持內建的值比較、with 表達式。

public record struct Point(int X, int Y);
var p1 = new Point(1,2);
var p2 = p1 with { X = 3 }; // 基於值的複製

高級用法

與模式匹配結合使用

public struct RGBColor
{
    public byte R;
    public byte G;
    public byte B;
    
    public RGBColor(byte r, byte g, byte b)
    {
        R = r;
        G = g;
        B = b;
    }
    
    public void Deconstruct(out byte r, out byte g, out byte b)
    {
        r = R;
        g = G;
        b = B;
    }
}

class Program
{
    static string GetColorName(RGBColor color)
    {
        return color switch
        {
            (255, 0, 0) => "Red",
            (0, 255, 0) => "Green",
            (0, 0, 255) => "Blue",
            (255, 255, 255) => "White",
            (0, 0, 0) => "Black",
            _ => "Unknown"
        };
    }
    
    static void Main()
    {
        RGBColor red = new RGBColor(255, 0, 0);
        Console.WriteLine(GetColorName(red)); // 輸出: Red
    }
}

適用場景

✅ 適合:

  • 輕量級、小數據對象(座標、顏色、矩形、數值容器等)。
  • 高性能計算(避免 GC 開銷)。
  • 頻繁創建銷燬的對象。

❌ 不適合:

  • 對象非常大(複製開銷高)。
  • 需要繼承層次。
  • 需要引用語義(共享同一對象)。
如果類型大小超過 16 字節,且頻繁傳遞,建議使用 class 而不是 struct

實戰示例

高性能點計算

public readonly struct Point
{
    public readonly double X;
    public readonly double Y;
    public Point(double x, double y) => (X, Y) = (x, y);

    public double Distance(Point other) =>
        Math.Sqrt(Math.Pow(X - other.X, 2) + Math.Pow(Y - other.Y, 2));
}

void Demo()
{
    Point p1 = new(1, 2);
    Point p2 = new(4, 6);
    Console.WriteLine(p1.Distance(p2)); // 5
}
  • GC 壓力。
  • 值傳遞保證線程安全。

總結

特性 説明
類型 值類型(Value Type)
內存分配 棧分配或嵌入堆中
繼承 不能繼承其他類/結構體,只能實現接口
構造函數 系統提供無參構造;用户可定義有參構造
默認初始化 所有字段自動初始化為默認值
傳遞方式 按值傳遞(複製整個對象)
典型場景 小型數據對象(Point、Color)、高性能計算
高級特性 readonly structref structrecord struct
設計建議 儘量保持不可變,避免裝箱,避免過大對象
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.