部分類別和方法 (C# 程式設計手冊)
有可能將 class、struct、interface 或方法的定義,分割到兩個以上的來源檔案。 每一個來源檔案都包含型別或方法定義的一個區段,而當編譯應用程式時,就會將所有區段結合起來。
部分類別
有幾種情況需要分割類別定義︰
- 透過不同的檔案宣告類別,可讓多位程式設計人員同時處理類別。
- 您可以將程式碼新增至類別,而不需要重新建立內含自動產生來源的來源檔案。 Visual Studio 建立 Windows Forms、Webb 服務包裝函式程式碼等等時,會使用這種方法。 您可以建立使用這些類別的程式碼,不必修改 Visual Studio 建立的檔案。
- 來源產生器可以在類別中產生額外的功能。
若要分割類別定義,請使用 部分 關鍵詞修飾詞。 實際上,每個部分類別通常會在個別的檔案中定義,讓您在一段時間內更容易管理和擴充類別。
下列 Employee
範例示範類別如何在兩個檔案之間分割:Employee_Part1.cs和Employee_Part2.cs。
// This is in Employee_Part1.cs
public partial class Employee
{
public void DoWork()
{
Console.WriteLine("Employee is working.");
}
}
// This is in Employee_Part2.cs
public partial class Employee
{
public void GoToLunch()
{
Console.WriteLine("Employee is at lunch.");
}
}
//Main program demonstrating the Employee class usage
public class Program
{
public static void Main()
{
Employee emp = new Employee();
emp.DoWork();
emp.GoToLunch();
}
}
// Expected Output:
// Employee is working.
// Employee is at lunch.
partial
關鍵字表示可在命名空間中定義類別、結構或介面的其他組件。 所有組件都必須使用 partial
關鍵字。 所有組件都必須可在編譯時間取得,以形成最後的型別。 所有組件必須有相同的存取範圍,例如 public
、private
等等。
如果任何組件宣告為抽象的,則整個型別視為抽象的。 如果任何組件宣告為密封的,則整個型別視為密封的。 如果任何組件宣告基底型別,則整個型別會繼承該類別。
指定基底類別的所有組件必須一致,但省略基底類別的組件仍會繼承基底型別。 組件可以指定不同的基底介面,而最後的型別會實作部分宣告列出的所有介面。 在部分定義中宣告的任何類別、結構或介面成員都可供所有其他組件使用。 最後的型別是所有組件在編譯時期的組合。
注意
partial
修飾詞不提供委派或列舉宣告使用。
下例會顯示可為部份的巢狀型別,即使巢狀型別所在的型別不是部份本身也是如此。
class Container
{
partial class Nested
{
void Test() { }
}
partial class Nested
{
void Test2() { }
}
}
在編譯時期會合併部分型別定義的屬性。 例如,請考慮下列宣告:
[SerializableAttribute]
partial class Moon { }
[ObsoleteAttribute]
partial class Moon { }
其與下列宣告相同:
[SerializableAttribute]
[ObsoleteAttribute]
class Moon { }
以下是合併自所有部分型別定義︰
- XML 註解。 不過,如果部分成員的兩個宣告都包含註解,系統只會納入實作成員的註解。
- interfaces
- 泛型型別參數屬性
- 類別屬性
- 成員
例如,請考慮下列宣告:
partial class Earth : Planet, IRotate { }
partial class Earth : IRevolve { }
其與下列宣告相同:
class Earth : Planet, IRotate, IRevolve { }
限制
當您處理部份類別定義時要遵循的數項規則:
- 表示同型別組件的所有部分型別定義都必須使用
partial
來修改。 例如,下列類別宣告會產生錯誤︰public partial class A { } //public class A { } // Error, must also be marked partial
partial
修飾元只能緊貼在關鍵字class
、struct
或interface
之前。- 部分型別定義中允許巢狀部分型別,如下例所示︰
partial class ClassWithNestedClass { partial class NestedClass { } } partial class ClassWithNestedClass { partial class NestedClass { } }
- 表示同型別組件的所有部分型別定義都必須在相同的組件和相同的模組 (.exe 或 .dll 檔案) 中定義。 部份定義不能跨越多個模組。
- 所有部分型別定義中的類別名稱和泛型型別參數必須相符。 泛型型別可以是部分的。 每個部分宣告都必須以相同的順序使用相同的參數名稱。
- 下列部分型別定義中的關鍵字是選擇性的,但如果它出現在一個部分型別定義中,相同型別的其他部分定義也必須指定相同關鍵字︰
如需詳細資訊,請參閱型別參數的條件約束。
範例
在下列範例中,類別的 Coords
欄位和建構函式會宣告在一個部分類別定義中(Coords_Part1.cs
),而方法 PrintCoords
則會在另一個部分類別定義中宣告 (Coords_Part2.cs
)。 此區隔示範如何跨多個檔案分割部分類別,以方便維護。
// This is in Coords_Part1.cs
public partial class Coords
{
private int x;
private int y;
public Coords(int x, int y)
{
this.x = x;
this.y = y;
}
}
// This is in Coords_Part2.cs
public partial class Coords
{
public void PrintCoords()
{
Console.WriteLine("Coords: {0},{1}", x, y);
}
}
// Main program demonstrating the Coords class usage
class TestCoords
{
static void Main()
{
Coords myCoords = new Coords(10, 15);
myCoords.PrintCoords();
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
// Output: Coords: 10,15
下例範例示範您也可以開發部分結構和介面。
partial interface ITest
{
void Interface_Test();
}
partial interface ITest
{
void Interface_Test2();
}
partial struct S1
{
void Struct_Test() { }
}
partial struct S1
{
void Struct_Test2() { }
}
部分成員
部分類別或結構可能包含部分成員。 類別的一部分包含成員的簽章。 可在相同組件或另一個組件中定義實作。
當簽章遵守下列規則時,就不需要實作部份方法:
沒有任何實作時,此方法與方法的所有呼叫都會在編譯時間遭到移除。
任何不符合上述所有限制的方法 (包括屬性和索引子),都必須提供實作。 「來源產生器」可能會提供該實作。 無法使用自動實作的屬性來實作部分屬性 。 編譯程式無法區分自動實作的屬性,以及部分屬性的宣告宣告。
從 C# 13 開始,部分屬性的實作宣告可以使用 字段支援的屬性 來定義實作宣告。 欄位支援的 屬性提供簡潔的語法 field
,其中 關鍵詞會存取屬性的編譯程式合成支援欄位。 例如,您可以撰寫下列內容:
// in file1.cs
public partial class PropertyBag
{
// Defining declaration
public partial int MyProperty { get; set; }
}
// In file2.cs
public partial class PropertyBag
{
// Defining declaration
public partial int MyProperty { get => field; set; }
}
您可以在或 set
存取子或兩者中使用。field
get
重要
關鍵詞 field
是 C# 13 中的預覽功能。 您必須使用 .NET 9,並將項目 <LangVersion>
檔中的 元素設定為 preview
,才能使用 field
內容關鍵詞。
您應該小心在 field
類別中使用關鍵詞功能,其具有名為 field
的欄位。 新的 field
關鍵詞會遮蔽屬性存取子範圍中名為 field
的欄位。 您可以變更變數的名稱 field
,或使用 @
權杖將識別元參考 field
為 @field
。 您可以閱讀 關鍵詞的功能規格field
來深入瞭解。
部分方法能夠讓類別的一部分實作者聲明成員。 類別另一部分的實作者則可定義該成員。 在兩種情況下此分離十分有用:產生重複使用程式碼的範本和來源產生器。
- 範本程式碼:範本會保留方法名稱和特徵標記,讓產生的程式碼可以呼叫方法。 這些方法會遵循限制,讓開發人員決定是否要實作方法。 如未實作方法,則編譯器會移除方法簽章和對方法的所有呼叫。 方法的呼叫,包括評估呼叫中的引數可能發生的任何結果,在執行階段沒有任何作用。 因此,部份類別中的任何程式碼都可以自由使用部份方法,即使不提供實作也是如此。 如已呼叫、但未實作方法,就不會產生任何編譯時間或執行階段錯誤。
- 來源產生器:來源產生器會為成員提供實作。 人類開發人員可新增成員宣告 (通常具備經過來源產生器讀取的屬性)。 開發人員可以撰寫程式碼來叫用這些成員。 來源產生器會在編譯期間執行,並提供實作。 這種情況下,系統通常不會遵循可能不會實作的部分成員限制。
// Definition in file1.cs
partial void OnNameChanged();
// Implementation in file2.cs
partial void OnNameChanged()
{
// method body
}
- 部分成員宣告必須以內容關鍵字 partial 開頭。
- 部分型別的兩個部分中的部分成員簽章必須相符。
- 部分成員可能有 static 和 unsafe 修飾詞。
- 部分成員可能是泛用的。 定義和實作方法宣告的條件約束必須相同。 參數和型別參數名稱在實作宣告中不必相同,但在定義宣告中則需相同。
- 您可以對已定義和實作的部份方法進行委派,但不能委派給沒有實作的部份方法。
C# 語言規格
如需詳細資訊,請參閱 C# 語言規格中的部份型別和部份方法。 語言規格是 C# 語法及用法的限定來源。 部分方法的新功能定義於功能規格中。