介面可包含的成員類型
- ✅ 方法、屬性、事件、索引子
- ❌ 常數、欄位、運算子、建構函式、解構函式、靜態成員(C# 8.0 前)
- C# 8.0+:可包含預設實作的方法、屬性、索引子、
static 成員(私有/公開)、private 方法
類別 vs 結構體:核心差異對照
| 維度 | 類別 | 結構體 |
| 型別分類 | 參考型別 | 值型別 |
| 記憶體配置 | 堆積 | 堆疊(或內嵌於物件/陣列) |
| 指派行為 | 複製參考(共用實例) | 複製值(產生獨立副本) |
| 預設值 | null | 所有欄位為 0/false/null |
| 繼承 | 單一繼承,可繼承/被繼承 | 不可繼承類別/結構,可實作介面 |
| 建構函式 | 參數化/無參數皆可 | 無參數建構函式隱含存在,不可自定;可定義參數化 |
| 解構函式 | 支援 | 不支援 |
| Null 可用性 | 天然可為 null | 需 Nullable<T> 或 T? |
| 封箱/解箱 | 無 | 轉為 object/介面時發生 |
實戰範例:點幾何物件設計
using System;
interface IPoint {
double X { get; set; }
double Y { get; set; }
double Z { get; set; }
}
// 結構體:輕量、不可變、高效能
struct Point : IPoint {
private readonly double _x, _y, _z;
public Point(double x, double y, double z) { _x = x; _y = y; _z = z; }
public double X { get => _x; set => throw new NotSupportedException("不可變"); }
public double Y { get => _y; set => throw new NotSupportedException("不可變"); }
public double Z { get => _z; set => throw new NotSupportedException("不可變"); }
}
// 類別:複雜邏輯、身分識別、可變狀態
class PointGeometry {
private Point _value;
public PointGeometry(double x, double y, double z) => _value = new Point(x, y, z);
public PointGeometry(Point value) => _value = value; // 值複製
public double X { get => _value.X; set => _value = new Point(value, _value.Y, _value.Z); }
public double Y { get => _value.Y; set => _value = new Point(_value.X, value, _value.Z); }
public double Z { get => _value.Z; set => _value = new Point(_value.X, _value.Y, value); }
public static PointGeometry operator +(PointGeometry a, PointGeometry b)
=> new PointGeometry(a.X + b.X, a.Y + b.Y, a.Z + b.Z);
public override string ToString() => $"X: {_value.X}, Y: {_value.Y}, Z: {_value.Z}";
}
class Program {
static void Main() {
Point p = new Point(1, 2, 3);
PointGeometry g1 = new PointGeometry(p);
PointGeometry g2 = new PointGeometry(p);
g2.X = 4; g2.Y = 5; g2.Z = 6;
Console.WriteLine(g1); // 1,2,3 (值複製,互不影響)
Console.WriteLine(g2); // 4,5,6
PointGeometry g3 = g1; // 參考複製
g1.X = 7; g1.Y = 8; g1.Z = 9;
Console.WriteLine(g1); // 7,8,9
Console.WriteLine(g3); // 7,8,9 (同一實例)
Console.ReadLine();
}
}
輸出結果
X: 1, Y: 2, Z: 3
X: 4, Y: 5, Z: 6
X: 7, Y: 8, Z: 9
X: 7, Y: 8, Z: 9
選擇指南:何時用結構體?
- ✅ 單一數值邏輯單元(
Point、Vector、DateTime、Guid、Decimal)
- ✅ 大量陣列/集合元素(減少堆積壓力、提升快取命中率)
- ✅ 不可變資料傳遞(避免防禦性複製)
- ✅ 高頻建立/銷毀(避免 GC 壓力)
選擇指南:何時用類別?
- ✅ 複雜業務邏輯、狀態機、生命週期管理
- ✅ 需要繼承/多型行為
- ✅ 身分識別語意(同一實例代表同一實體)
- ✅ 需要
null 表示「不存在」
- ✅ 實作介面時避免封箱(介面參考會封箱結構體)
效能迷思澄清
- 結構體不一定更快:大型結構體(>16 bytes)參數傳遞、回傳、介面轉換會產生大量複製/封箱開銷
- readonly 結構體:C# 7.2+
readonly struct 避免防禦性複製,編譯器保證不修改
- ref struct:C# 7.2+
ref struct 僅能棧上分配(如 Span<T>),極致效能