partial 讓單一類別、結構、介面或委派的定義可分散在多個 .cs 檔案中。編譯時編譯器會將其合併為單一型別。C# 2.0 引入,主要解決:
// File: Customer.Data.cs
public partial class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
// File: Customer.Behavior.cs
public partial class Customer
{
public void SendWelcomeEmail() => Console.WriteLine($"Welcome {Name}!");
public bool IsVip => Id < 100;
}
編譯後等同於單一檔案完整定義。
| 規則 | 說明 |
|---|---|
| 適用目標 | class、struct、interface、delegate(C# 17+) |
| 所有部分 | 皆須標記 partial |
| 命名空間 | 所有部分必須在相同命名空間 |
| 編譯單元 | 所有部分必須在同一專案同時編譯 |
| 基類/介面 | 各部分指定的基類/介面必須一致(可僅在一處指定) |
| 存取修飾詞 | 各部分的類別存取修飾詞必須一致 |
| abstract / sealed | 任一部分標記 → 整個型別視為 abstract/sealed |
| 衝突禁止 | 不可同時有 abstract 與 sealed、不可有衝突的基類 |
// File1.cs
[Serializable, Obsolete("v1")]
partial class Entity { }
// File2.cs
[DataContract, Obsolete("v2")]
partial class Entity { }
// 編譯後等同於:
[Serializable, Obsolete("v1"), DataContract, Obsolete("v2")]
class Entity { }
Obsolete 會全部保留)讓產生器宣告「掛點」,開發者選擇性實作。若無實作,呼叫端與宣告皆會被編譯器移除(零成本)。
partial void MethodName(parameters)void 回傳、不可有 out 參數private,不可標記存取修飾詞static、unsafe、泛型// 產生器產出
public partial class Order
{
partial void OnValidateSave();
public void Save()
{
OnValidateSave(); // 若無實作,此行編譯後消失
// 存資料庫邏輯
}
}
// 開發者實作
public partial class Order
{
partial void OnValidateSave()
{
if (Amount <= 0) throw new InvalidOperationException("金額需大於 0");
}
}
| 場景 | 建議檔案命名 | 內容 |
|---|---|---|
| 資料實體 | Entity.cs / Entity.Generated.cs | 屬性 / 產生器產出 |
| 業務邏輯 | Entity.Behavior.cs | 方法、驗證、領域事件 |
| 資料存取 | Entity.Repository.cs | 查詢擴充、儲存邏輯 |
| 序列化/DTO | Entity.Serialization.cs | JsonConverter、映射設定 |
| UI 相關 | Entity.ViewModel.cs | 格式化、顯示邏輯 |
partial 用於方法分割(除非是分部方法)[GeneratedCode] 屬性,便於工具識別partial 是「編譯時合併」的語法糖,零執行期成本。善用它可實現關注點分離、產生器與手寫程式碼共存、大型團隊並行開發。核心原則:單一職責原則延伸至檔案層級——每個 .cs 檔只負責一件事。