簡介
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) | 分配快,生命週期短時更高效 |
構造函數與初始化
默認構造函數
- 所有結構體都有一個隱式的無參構造函數,將所有字段初始化為 默認值(如
0、null)。 - 用户不能顯式定義無參構造函數(
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 struct、ref struct、record struct |
| 設計建議 | 儘量保持不可變,避免裝箱,避免過大對象 |