C# 源文件可以包含结构化注释,这些注释为这些文件中定义的类型生成 API 文档。 C# 编译器生成一个 XML 文件,其中包含表示注释和 API 签名的结构化数据。 例如,其他工具可以处理 XML 输出以网页或 PDF 文件的形式创建人工可读文档。
此过程为在代码中添加 API 文档提供了许多优势:
- C# 编译器将 C# 代码的结构与注释的文本合并到单个 XML 文档中。
- C# 编译器验证注释是否与相关标记的 API 签名匹配。
- 处理 XML 文档文件的工具可以定义特定于这些工具的 XML 元素和属性。
Visual Studio 等工具为文档注释中使用的许多常见 XML 元素提供 IntelliSense。
本文介绍以下主题:
- 文档注释和 XML 文件生成
- 由 C# 编译器和 Visual Studio 验证的标记
- 生成的 XML 文件的格式
通过编写三斜杠指示的特殊注释字段,为代码创建文档。 注释字段包括描述注释后面的代码块的 XML 元素。 例如:
/// <summary>
/// This class performs an important function.
/// </summary>
public class MyClass { }
设置 GenerateDocumentationFile 或 DocumentFile 选项,编译器查找源代码中带有 XML 标记的所有注释字段,并从这些注释创建 XML 文档文件。 启用此选项后,编译器将为项目中声明的任何公开可见成员生成 CS1591 警告,而无需 XML 文档注释。
XML 文档注释的使用需要分隔符来指示文档注释的开始和结束位置。 您将以下分隔符用于 XML 文档标签:
///
单行分隔符:文档示例和 C# 项目模板使用此表单。 如果空格位于分隔符后面,则 XML 输出中不包含该空格。备注
在代码编辑器中键入
///
分隔符后,Visual Studio 会自动插入<summary>
</summary>
和标记并将光标置于这些标记中。 可以在 “选项”对话框中打开或关闭此功能。/** */
多行分隔符:/** */
分隔符具有以下格式规则:在包含
/**
分隔符的行上,如果行的其余部分为空格,则不会为注释处理该行。 如果分隔符后面的/**
第一个字符为空格,则忽略该空格字符,并处理该行的其余部分。 否则,分隔符后面的/**
行的整个文本将作为注释的一部分进行处理。在包含
*/
分隔符的行上,如果直到*/
分隔符只有空格,则忽略该行。 否则,将*/
分隔符之前的行的文本作为注释的一部分进行处理。对于以分隔符开头
/**
的行之后的行,编译器会在每行的开头查找一个通用模式。 该模式可以包含可选的空格和/或星号(*
),后跟更多可选空格。 如果编译器在每行开头发现一个不以/**
分隔符开头且不以*/
分隔符结尾的通用模式,它会忽略该模式。以下注释中将被处理的唯一部分是以
<summary>
开头的行。 这三种标记格式生成相同的注释。/** <summary>text</summary> */ /** <summary>text</summary> */ /** * <summary>text</summary> */
编译器标识第二行和第三行开头的“*”的常见模式。 该模式不包括在输出中。
/** * <summary> * text </summary>*/
编译器在以下注释中找不到常见模式,因为第三行的第二个字符不是星号。 第二行和第三行中的所有文本都作为注释的一部分进行处理。
/** * <summary> text </summary> */
编译器在以下注释中找不到模式,原因有两个。 首先,星号之前的空格数不一致。 其次,第 5 行以制表符开头,这与空格不匹配。 第二行至五行中的所有文本都作为批注的一部分进行处理。
/** * <summary> * text * text2 * </summary> */
若要引用 XML 元素(例如,函数处理要在 XML 文档注释中描述的特定 XML 元素),可以使用标准引用机制(<
以及 >
)。 若要在代码引用 (cref
) 元素中引用泛型标识符,可以使用转义字符(例如) cref="List<T>"
或大括号(cref="List{T}"
)。 作为特殊情况,编译器将大括号解析为尖括号,以便作者在引用泛型标识符时,文档注释显得不那么繁琐。
备注
如果使用单行 XML 注释分隔符编写注释, ///
但不包含任何标记,编译器会将这些注释的文本添加到 XML 输出文件。 但是,输出不包含 XML 元素,如 <summary>
. 使用 XML 注释(包括 Visual Studio IntelliSense)的大多数工具不会阅读这些注释。
以下工具从 XML 注释创建输出:
- DocFX: DocFX 是适用于 .NET 的 API 文档生成器,目前支持 C#、Visual Basic 和 F#。 它还允许自定义生成的参考文档。 DocFX 从源代码和 Markdown 文件生成静态 HTML 网站。 此外,DocFX 还提供通过模板自定义网站的布局和样式的灵活性。 还可以创建自定义模板。
- Sandcastle: Sandcastle 工具 为包含概念页和 API 参考页的托管类库创建帮助文件。 Sandcastle 工具基于命令行,没有 GUI 前端、项目管理功能或自动化生成过程。 Sandcastle 帮助文件生成器提供独立的 GUI 和基于命令行的工具,以自动化方式生成帮助文件。 Visual Studio 集成包也可用于它,以便可以从 Visual Studio 中完全创建和管理帮助项目。
- Doxygen: Doxygen 从一组记录的源文件生成联机文档浏览器(HTML)或脱机参考手册(在 LaTeX 中)。 还支持在 RTF(MS Word)、PostScript、超链接 PDF、压缩 HTML、DocBook 和 Unix 手动页中生成输出。 可以将 Doxygen 配置为从未记录的源文件中提取代码结构。
备注
XML 文档注释不是元数据;它们不包括在编译的程序集中,因此无法通过反射访问它们。
每个类型或成员都存储在输出 XML 文件中的元素中。 其中每个元素都有一个唯一的 ID 字符串,用于标识类型或成员。 ID 字符串必须考虑运算符、参数、返回值、泛型类型参数、ref
in
和out
参数。 若要对所有这些潜在元素进行编码,编译器遵循明确定义的规则来生成 ID 字符串。 处理 XML 文件的程序使用 ID 字符串标识文档适用的相应 .NET 元数据或反射项。
编译器在生成 ID 字符串时会遵守以下规则:
字符串中没有空格。
字符串的第一部分使用单个字符后跟冒号标识成员的类型。 使用以下成员类型:
字符 成员类型 注释 N
namespace 不能向命名空间添加文档注释,但可以在支持的情况下创建对它们的 cref
引用。T
类型 类型是类、接口、结构、枚举或委托。 F
field P
property 包括索引器或其他索引属性。 M
方法 包括特殊方法,如构造函数和运算符。 E
event !
错误字符串 其余字符串提供有关错误的信息。 C# 编译器为无法解析的链接生成错误信息。 字符串的第二部分是项的完全限定名称,从命名空间的根目录开始。 项的名称、其封闭类型和命名空间通过句点进行分隔。 如果项本身的名称有句点,则它们将被替换为哈希符号 ('#')。 语法假定没有项直接在其名称中具有哈希符号。 例如,String 构造函数的完全限定名称为“System.String.#ctor”。
对于属性和方法,括在括号中的参数列表如下。 如果没有参数,则不存在括号。 参数用逗号分隔。 每个参数的编码直接遵循在 .NET 签名中编码的方式(请参阅 Microsoft.VisualStudio.CorDebugInterop.CorElementType 以下列表中所有 caps 元素的定义):
- 基类型。 常规类型 (
ELEMENT_TYPE_CLASS
或ELEMENT_TYPE_VALUETYPE
) 表示为类型的完全限定名称。 - 内部类型(例如,、
ELEMENT_TYPE_I4
、ELEMENT_TYPE_OBJECT
ELEMENT_TYPE_STRING
和ELEMENT_TYPE_TYPEDBYREF
ELEMENT_TYPE_VOID
)表示为相应完整类型的完全限定名称。 例如,System.Int32
或System.TypedReference
。 -
ELEMENT_TYPE_PTR
的表示形式为:在修改类型之后添加“*”。 -
ELEMENT_TYPE_BYREF
的表示形式为:在修改类型之后添加“@”。 -
ELEMENT_TYPE_CMOD_OPT
的表示形式为:在修改类型之后添加“!”和修饰符类的完全限定名称。 ELEMENT_TYPE_SZARRAY
在数组的元素类型之后表示为“[]”。ELEMENT_TYPE_ARRAY
表示为 [下限:size
,下限:size
],其中逗号数为排名 - 1,并且每个维度的下限和大小(如果已知)以十进制表示。 如果未指定下限和大小,则会省略它们。 如果省略特定维度的下限和大小,则也会省略“:”。 例如,一个下限为 1 且大小未指定的二维数组为 [1:,1:]。
- 基类型。 常规类型 (
仅对于转换运算符(
op_Implicit
和op_Explicit
),方法的返回值将编码为~
后跟返回类型。 例如,<member name="M:System.Decimal.op_Explicit(System.Decimal arg)~System.Int32">
是在System.Decimal
类中声明的强制转换运算符public static explicit operator int (decimal value);
的标记。对于泛型类型,类型名称后跟反引号,然后是指示泛型类型参数数量的数字。 例如:
<member name="T:SampleClass`2">
是定义为public class SampleClass<T, U>
的类型标记。 对于采用泛型类型作为参数的方法,泛型类型参数被指定为以反杆开头的数字(例如“0,”1)。 每个数字表示类型泛型参数的从零开始的数组表示法。-
ELEMENT_TYPE_PINNED
的表示形式为:在修改类型之后添加“^”。 C# 编译器永远不会生成此编码。 ELEMENT_TYPE_CMOD_REQ
被表示为“|”,并在修改后的类型之后显示修饰符类的完全限定名称。 C# 编译器永远不会生成此编码。ELEMENT_TYPE_GENERICARRAY
在数组的元素类型之后表示为“[?]”。 C# 编译器永远不会生成此编码。ELEMENT_TYPE_FNPTR
表示为“=FUNC:type
(signature)”,其中type
返回类型, 签名 是方法的参数。 如果没有参数,则省略括号。 C# 编译器永远不会生成此编码。- 不表示下列签名组件,因为这些组件不用于区分重载方法:
- 调用约定
- 返回类型
ELEMENT_TYPE_SENTINEL
-
以下示例演示如何生成类及其成员的 ID 字符串:
namespace MyNamespace;
/// <summary>
/// Enter description here for class X.
/// ID string generated is "T:MyNamespace.MyClass".
/// </summary>
public unsafe class MyClass
{
/// <summary>
/// Enter description here for the first constructor.
/// ID string generated is "M:MyNamespace.MyClass.#ctor".
/// </summary>
public MyClass() { }
/// <summary>
/// Enter description here for the second constructor.
/// ID string generated is "M:MyNamespace.MyClass.#ctor(System.Int32)".
/// </summary>
/// <param name="i">Describe parameter.</param>
public MyClass(int i) { }
/// <summary>
/// Enter description here for field Message.
/// ID string generated is "F:MyNamespace.MyClass.Message".
/// </summary>
public string? Message;
/// <summary>
/// Enter description for constant PI.
/// ID string generated is "F:MyNamespace.MyClass.PI".
/// </summary>
public const double PI = 3.14;
/// <summary>
/// Enter description for method Func.
/// ID string generated is "M:MyNamespace.MyClass.Func".
/// </summary>
/// <returns>Describe return value.</returns>
public int Func() => 1;
/// <summary>
/// Enter description for method SomeMethod.
/// ID string generated is "M:MyNamespace.MyClass.SomeMethod(System.String,System.Int32@,System.Void*)".
/// </summary>
/// <param name="str">Describe parameter.</param>
/// <param name="num">Describe parameter.</param>
/// <param name="ptr">Describe parameter.</param>
/// <returns>Describe return value.</returns>
public int SomeMethod(string str, ref int num, void* ptr) { return 1; }
/// <summary>
/// Enter description for method AnotherMethod.
/// ID string generated is "M:MyNamespace.MyClass.AnotherMethod(System.Int16[],System.Int32[0:,0:])".
/// </summary>
/// <param name="array1">Describe parameter.</param>
/// <param name="array">Describe parameter.</param>
/// <returns>Describe return value.</returns>
public int AnotherMethod(short[] array1, int[,] array) { return 0; }
/// <summary>
/// Enter description for operator.
/// ID string generated is "M:MyNamespace.MyClass.op_Addition(MyNamespace.MyClass,MyNamespace.MyClass)".
/// </summary>
/// <param name="first">Describe parameter.</param>
/// <param name="second">Describe parameter.</param>
/// <returns>Describe return value.</returns>
public static MyClass operator +(MyClass first, MyClass second) { return first; }
/// <summary>
/// Enter description for property.
/// ID string generated is "P:MyNamespace.MyClass.Prop".
/// </summary>
public int Prop { get { return 1; } set { } }
/// <summary>
/// Enter description for event.
/// ID string generated is "E:MyNamespace.MyClass.OnHappened".
/// </summary>
public event Del? OnHappened;
/// <summary>
/// Enter description for index.
/// ID string generated is "P:MyNamespace.MyClass.Item(System.String)".
/// </summary>
/// <param name="str">Describe parameter.</param>
/// <returns></returns>
public int this[string s] => 1;
/// <summary>
/// Enter description for class Nested.
/// ID string generated is "T:MyNamespace.MyClass.Nested".
/// </summary>
public class Nested { }
/// <summary>
/// Enter description for delegate.
/// ID string generated is "T:MyNamespace.MyClass.Del".
/// </summary>
/// <param name="i">Describe parameter.</param>
public delegate void Del(int i);
/// <summary>
/// Enter description for operator.
/// ID string generated is "M:MyNamespace.MyClass.op_Explicit(MyNamespace.MyClass)~System.Int32".
/// </summary>
/// <param name="myParameter">Describe parameter.</param>
/// <returns>Describe return value.</returns>
public static explicit operator int(MyClass myParameter) => 1;
}
有关详细信息,请参阅有关文档注释的 C# 语言规范 附件。