分部(方法)(C# 参考)

分部成员有一个声明声明,并且通常有一个实现声明声明声明不包括正文。 实现声明提供成员正文。 通过分部方法,类设计器可提供与事件处理程序类似的方法挂钩,以便开发者决定是否实现。 分部类型和成员会为人类开发人员提供一种编写类型部分的方法,而工具则编写该类型的其他部分。 如果开发人员不提供可选的实现声明,则编译器可以在编译时删除声明声明。 以下条件适用于分部成员:

  • 声明必须以上下文关键字 partial 开头。
  • 分部类型各部分中的签名必须匹配。

不允许对构造函数、终结器、重载运算符或事件声明使用 partial 关键字。 在 C# 13 之前,不允许对属性或索引器使用 partial

在以下情况下,分部方法不需要可实现声明:

任何不符合所有这些限制的成员(例如 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;
    }
}

上述示例演示了关于如何组合这两个声明的规则:

  • 签名匹配:一般情况下,声明声明和实现声明的签名必须匹配。 这包括方法、属性、索引器和单个访问器的辅助功能修饰符。 它包括所有参数的参数类型和 ref 类型修饰符。 返回类型和任何 ref 类型修饰符都必须匹配。 元组成员名称必须匹配。 但是,某些规则很灵活:
    • 声明声明和实现声明可以具有不同的可为 null 的注释设置。 这意味着,一个可以是 可为 null 的未知声明,另一个是 已启用可为 null 的声明
    • 不涉及未知 Null 性的 Null 性差异会生成警告。
    • 默认参数值不需要匹配。 如果方法或索引器的实现声明声明默认参数值,编译器会发出警告。
    • 当参数名称不匹配时,编译器会发出警告。 发出的 IL 包含声明声明的参数名称。
  • 文档注释:可以包括任一声明中的文档注释。 如果声明声明和实现声明都包括文档注释,则包括实现声明中的注释。 在上述示例中,文档注释包括:
    • 对于 Capacity 属性,注释取自实现声明。 当两个声明都有 /// 注释时,会使用实现声明注释。
    • 对于索引器,注释取自声明声明。 实现声明不包括任何 /// 注释。
    • 对于 TryGetAt,注释取自实现声明。 声明声明不包括任何 /// 注释。
    • 生成的 XML 具有所有 public 成员的文档注释。
  • 大多数属性声明会组合在一起。 但是,所有调用方信息属性都使用 AllowMultiple=false 来定义。 编译器可识别声明声明上的任何调用方信息属性。 系统会忽略实现声明上的所有调用方信息属性。 如果在实现声明上添加调用方信息属性,则编译器将发出警告。

另请参阅