部分成員 (C# 參照)
部分成員包含一個宣告宣告,通常還有一個實作宣告。 宣告宣告不含主體。 實作宣告會提供成員的主體。 部分成員可讓類別設計工具提供成員攔截程序,可以由工具 (例如來源產生器) 來實作。 部分型別和成員提供一套方式,讓人類開發人員撰寫型別的一部分,而工具則撰寫型別的其他部分。 如果開發人員未提供選擇性的實作宣告,編譯器可以在編譯階段移除宣告宣告。 下列條件適用於部分成員:
- 宣告必須以內容關鍵字 partial 作為開頭。
- 部分型別的兩個部分中的簽章必須相符。
建構函式、完成項、多載運算子或事件宣告無法使用 partial
關鍵字。 在 C# 13 之前,屬性或索引子無法使用 partial
。
以下案例並不需要部分方法,即有實作宣告:
- 它沒有任何存取範圍修飾符 (包括預設的
private
)。 - 它會傳回
void
。 - 它不具任何
out
參數。 - 它沒有以下任何修飾詞:
virtual
、override
、sealed
、new
或extern
。
任何不符上述所有限制的成員 (例如 public virtual partial void
方法),都必須提供實作。 部分屬性和索引子必須具有實作。
下列範例所示為符合上述限制的部分方法:
partial class MyPartialClass
{
// Declaring definition
partial void OnSomethingHappened(string s);
}
// This part can be in a separate file.
partial class MyPartialClass
{
// Comment out this method and the program
// will still compile.
partial void OnSomethingHappened(string s) =>
Console.WriteLine($"Something happened: {s}");
}
部分成員在與來源產生器結合使用時也很實用。 例如,下列模式可用來定義 RegEx:
public partial class RegExSourceGenerator
{
[GeneratedRegex("cat|dog", RegexOptions.IgnoreCase, "en-US")]
private static partial Regex CatOrDogGeneratedRegex();
private static void EvaluateText(string text)
{
if (CatOrDogGeneratedRegex().IsMatch(text))
{
// Take action with matching text
}
}
}
上述範例所示為必須具有實作宣告的部分方法。 在組建中,規則運算式來源產生器會建立實作宣告。
下列範例所示為類別的宣告宣告和實作宣告。 因為方法的傳回型別不是 void
(而是 string
),且其存取權是 public
,所以方法必須有實作宣告:
// Declaring declaration
public partial class PartialExamples
{
/// <summary>
/// Gets or sets the number of elements that the List can contain.
/// </summary>
public partial int Capacity { get; set; }
/// <summary>
/// Gets or sets the element at the specified index.
/// </summary>
/// <param name="index">The index</param>
/// <returns>The string stored at that index</returns>
public partial string this[int index] { get; set; }
public partial string? TryGetAt(int index);
}
public partial class PartialExamples
{
private List<string> _items = [
"one",
"two",
"three",
"four",
"five"
];
// Implementing declaration
/// <summary>
/// Gets or sets the number of elements that the List can contain.
/// </summary>
/// <remarks>
/// If the value is less than the current capacity, the list will shrink to the
/// new value. If the value is negative, the list isn't modified.
/// </remarks>
public partial int Capacity
{
get => _items.Count;
set
{
if ((value != _items.Count) && (value >= 0))
{
_items.Capacity = value;
}
}
}
public partial string this[int index]
{
get => _items[index];
set => _items[index] = value;
}
/// <summary>
/// Gets the element at the specified index.
/// </summary>
/// <param name="index">The index</param>
/// <returns>The string stored at that index, or null if out of bounds</returns>
public partial string? TryGetAt(int index)
{
if (index < _items.Count)
{
return _items[index];
}
return null;
}
}
上述範例說明這兩種宣告結合的規則:
- 簽章相符:一般而言,宣告宣告和實作宣告的簽章必須相符。 這包括方法、屬性、索引子和個別存取子上的存取範圍修飾詞。 其中包含所有參數的參數型別和類參照修飾詞。 傳回型別和任何類參照修飾詞都必須相符。 Tuple 成員的名稱必須相符。 不過,某些規則是有彈性的:
- 宣告宣告和實作宣告可能有不同的可為 Null 註釋設定。 也就是說,可能一個是可為 Null 遺忘式,另一個是啟用可為 Null。
- Null 屬性的差異若與遺忘式 Null 屬性無關,就會產生警告。
- 預設參數值不需相符。 如果方法或索引子的實作宣告宣告了預設參數值,編譯器會發出警告。
- 參數名稱不相符時,編譯器會發出警告。 發出的 IL 包含宣告宣告的參數名稱。
- 文件註解:任一種宣告都可能包含文件註解。 如果宣告宣告和實作宣告都包含文件註解,系統會納入實作宣告中的註解。 在上述範例中,文件註解包括:
- 如果是
Capacity
屬性,註解會取自實作宣告。 兩個宣告都有///
註解時,系統會使用實作宣告的註解。 - 如果是索引子,註解會取自宣告宣告。 實作宣告不包含任何
///
註解。 - 如果是
TryGetAt
,註解會取自實作宣告。 宣告宣告不包含任何///
註解。 - 產生的 XML 具有所有
public
成員的文件註解。
- 如果是
- 大部分屬性宣告都會合併。 不過,所有呼叫端資訊屬性都是以
AllowMultiple=false
來定義。 編譯器會識別出宣告宣告的任何呼叫端資訊屬性。 實作宣告的所有呼叫端資訊屬性都會遭到忽略。 如果您對實作宣告新增呼叫端資訊屬性,編譯器會發出警告。