
12.2 XML注释
XML注释是包含在代码中的专用注释。在构建项目时,Visual Studio可以根据这些注释生成一个XML文件,为用户定义的类型(如类)和类的各个成员(是用户定义的,也可能不是)提供信息,包括事件、函数以及属性。
XML注释可以包含XML标记和HTML标记的任意组合。Visual Studio会对一组预定义的标记进行特别的处理(本章将对此进行详细介绍),而其他标记则原样包含在生成的文档文件中。
12.2.1 添加XML注释
XML注释直接添加到与其关联的属性、方法或者类定义之前。对于C#,在成员或类声明前输入快捷代码///,Visual Studio就会自动添加XML注释块。在某些情况下,由项目模板生成的代码已经包含了XML注释,如图12-1所示。

图12-1
在Visual Studio选项中可以启用或禁用summary区域的自动插入功能。选择Tools | Options,再从导航树中选择Text Editor | C# | Advanced,取消选中Generate XML Documentation Comments for ///选项,就可以禁用这个功能。
可以使用“'”快捷代码为VB添加XML注释,这与C#文档的生成方式相同。
在这两种语言中,一旦添加了注释,Visual Studio就会自动在窗口的左边界上添加一个折叠区域。这样开发人员在编写代码时可以隐藏文档。把光标停放在折叠的区域上,就会显示一个包含注释块前几行内容的工具提示消息。
12.2.2 XML注释标记
可使用任意的XML注释结构,包括自己定制的XML标记。Visual Studio的XML注释处理器能够识别很多预定义的标记,并自动格式化它们。Sandcastle文档编译器(参见本章后面的内容)支持许多其他标记,还可以用自己的XML模式文档进一步补充这些标记。
如果需要在文档注释的文本中使用尖括号,应使用实体引用<和>。
因为文档非常重要,所以下一节详细介绍预定义标记、标记的语法以及在文档中的使用方式。
1.<c>标记
<c>标记内的文本应格式化为代码,而不是普通文本。此标记用于显示包含在普通文本块中的代码。<c>的结构非常简单,位于<c>和</c>之间的所有文本都显示为代码样式。
<c>code-formatted text</c>
下面的例子展示了如何在属性的描述中使用<c>标记。
C#
/// <summary>
/// The <c>UserId</c> property is used in conjunction with other properties
/// to set up a user properly.Remember to set the <c>Password</c> field too.
/// </summary>
public string UserId { get; set; }
VB
''' <summary>
''' The <c>UserId</c> property is used in conjunction with other properties
''' to set up a user properly.Remember to set the <c>Password</c> field too.''' </summary>
Public Property UserId()As String
2.<code>标记
如果文档中需要格式化为代码的文本不仅是普通文本块中的一个短语,则可以用<code>标记代替<c>。此标记把其内部的所有内容都标记为代码,但它是一个块级标记,而不是字符级标记。<code>标记的语法非常简单——一个起始标记和一个结束标记,中间是要格式化的文本,如下所示:
<code> Code-formatted text Code-formatted text </code>
<code>标记可以嵌套在其他任意的XML注释标记中。在下面的代码中,将此标记应用在属性定义的summary区域中:
C#
/// <summary>
/// The <c>UserId</c> property is used in conjunction with other properties
/// to set up a user properly.Remember to set the <c>Password</c> field too.
/// For example:
/// <code>
/// myUser.UserId = "daveg"
/// myUser.Password = "xg4*Wv"
/// </code>
/// </summary>
public string UserId { get; set; }
VB
''' <summary>
''' The <c>UserId</c> property is used in conjunction with other properties
''' to set up a user properly.Remember to set the <c>Password</c> field too.
''' For example:
''' <code>
''' myUser.UserId = "daveg"
''' myUser.Password = "xg4*Wv"
''' </code>
''' </summary>
Public Property UserId()As String
3.<exampIe>标记
内部文档的一个基本要求是为某个过程或者成员提供使用示例。<example>标记包含了相关成员的使用示例,并在文档中是一个独立的区域。尽管该标记只是帮助组织文档的结构,但是如果同时使用设计精妙的XML样式表或者处理指令,那么示例代码就可以以一种良好的格式呈现出来。
其他XML注释标记,如<c>和<code>,都可以包含在<example>标记内部的文本中,提供综合的文档示例。<example>是一个块级标记,其语法非常简单:
<example> Any sample text goes here. </example>
使用之前讨论的示例,下列代码把<code>格式化文本从<summary>部分移到<example>部分:
C#
/// <summary>
/// The <c>UserId</c> property is used in conjunction with other properties
/// to set up a user properly.Remember to set the <c>Password</c> field too.
/// </summary>
/// <example>
/// <code>
/// myUser.UserId = "daveg"
/// myUser.Password = "xg4*Wv"
/// </code>
/// </example>
public string UserId { get; set; }
VB
''' <summary>
''' The <c>UserId</c> property is used in conjunction with other properties
''' to set up a user properly.Remember to set the <c>Password</c> field too.
''' </summary>
''' <example>
''' <code>
''' myUser.UserId = "daveg"
''' myUser.Password = "xg4*Wv"
''' </code>
''' </example>
Public Property UserId()As String
4.<exception>标记
<exception>标记定义了与当前XML文档块关联的成员所抛出的异常。每个可抛出的异常都应定义在它自己的<exception>块中,并使用cref特性标注所抛出异常的完全限定类型名。注意,Visual Studio 2015 XML注释处理器会检查异常块的语法,强制包含cref特性。还可以确保不同的<exception>块不会使用同一个特性值。完整的语法如下所示:
<exception cref="exceptionName"> Exception description. </exception>
扩展前面讨论标记时给出的例子,下面的代码为与UserId属性关联的XML注释添加了两个异常的定义:System.TimeoutException和System.UnauthorizedAccessException。
C#
/// <summary>
/// The <c>UserId</c> property is used in conjunction with other properties
/// to set up a user properly.Remember to set the <c>Password</c> field too.
/// </summary>
/// <exception cref="System.TimeoutException">
/// Thrown when the code cannot determine if the user is valid within a reasonable
/// amount of time.
/// </exception>
/// <exception cref="System.UnauthorizedAccessException">
/// Thrown when the user identifier is not valid within the current context.
/// </exception>
/// <example>
/// <code>
/// myUser.UserId = "daveg"
/// myUser.Password = "xg4*Wv"
/// </code>
/// </example>
public string UserId { get; set; }
VB
''' <summary>
''' The <c>UserId</c> property is used in conjunction with other properties
''' to set up a user properly.Remember to set the <c>Password</c> field too.
''' </summary>
''' <exception cref="System.TimeoutException">
''' Thrown when the code cannot determine if the user is valid within a reasonable
''' amount of time.
''' </exception>
''' <exception cref="System.UnauthorizedAccessException">
''' Thrown when the user identifier is not valid within the current context.
''' </exception>
''' <example>
''' <code>
''' myUser.UserId = "daveg"
''' myUser.Password = "xg4*Wv"
''' </code>
''' </example>
Public Property UserId()As String
5.<incIude>标记
文档通常需要在多个项目之间共享。有时一个人负责编写文档,其他人负责编码。无论哪种情况,<include>标记都非常有效。<include>标记允许引用一个单独XML文件中的注释,与文档的其他内容内联在一起。使用这种方法,可以把文档与代码分开。如果注释非常长,那么这种处理可以使代码更加简洁。
使用<include>标记时,其语法要求指定要在当前位置引入外部文件的哪个部分。path特性使用标准的XPath语法标识XML节点的路径:
<include file="filename" path="XPathQuery" />
包含其他文档的外部XML文件必须有一个可通过使用XPath符号到达的部分,在path特性中指定该符号。此外,XPath值必须能够唯一地标识要包括的XML文档的特定部分。
在Visual Basic或者C#中可以使用相同的标记来引入文件。下面的代码修改前面讨论<exception>标记时的示例,把文档移动到外部文件中:
C#
/// <include file="externalFile.xml" path="MyDoc/Properties[@name='UserId']/*" />
public string UserId { get; set; }
VB
''' <include file="externalFile.xml" path="MyDoc/Properties[@name='UserId']/*" />
Public Property UserId()As String
用下面的XML文档结构来填充外部文件的内容,使它与<include>标记处理的内容一致:
<MyDoc> <Properties name="UserId"> <summary> The <c>sender</c> object is used to identify who invoked the procedure. </summary> <summary> The <c>UserId</c> property is used in conjunction with other properties to set up a user properly.Remember to set the <c>Password</c> field too. </summary> <exception cref="System.TimeoutException"> Thrown when the code cannot determine if the user is valid within a reasonable amount of time. </exception> <exception cref="System.UnauthorizedAccessException"> Thrown when the user identifier is not valid within the current context. </exception> <example> <code> myUser.UserId = "daveg" myUser.Password = "xg4*Wv" </code> </example> </Procedures> </MyDoc>
6.<list>标记
一些文档需要列举各种描述。使用<list>标记,可以创建有编号或者无编号的列表,以及由两列组成的表。这3种列举方式为列表中的每一项提供了两个参数:term和description。这些参数由各自的XML标记表示,指示处理器用不同的方式生成文档。
要在文档中创建列表,可以使用下面的语法。其中type的值可以是bullet、numbered或者table。
<list type="type"> <listheader> <term>termName</term> <description>description</description> </listheader> <item> <term>myTerm</term> <description>myDescription</description> </item> </list>
<listheader>块是可选的,它通常用于表样式的列表或定义列表。定义列表必须包含<term>标记,但项目列表、编号列表以及表可以省略<term>标记。
可以使用XML样式表格式化这些类型列表的XML。下面的例子使用了<list>标记。注意,代码省略了<listheader>标记,因为项目列表不需要它:
C#
/// <summary>
/// This function changes a user's password.The password change could fail for
/// several reasons:
/// <list type="bullet">
/// <item>
/// <term>Too Short</term>
/// <description>The new password was not long enough.</description>
/// </item>
/// <item>
/// <term>Not Complex</term>
/// <description>The new password did not meet the complexity requirements.It
/// must contain at least one of the following characters: lowercase, uppercase,
/// and number.
/// </description>
/// </item>
/// </list>
/// </summary>
public bool ChangePwd(string oldPwd, string newPwd)
{
//...code...
return true;
}
VB
''' <summary>
''' This function changes a users password.The password change could fail for
''' several reasons:
''' <list type="bullet">
''' <item>
''' <term>Too Short</term>
''' <description>The new password was not long enough.</description>
''' </item>
''' <item>
''' <term>Not Complex</term>
''' <description>The new password did not meet the complexity requirements.It
''' must contain at least one of the following characters: lowercase, uppercase,
''' and number.
''' </description>
''' </item>
''' </list>
''' </summary>
Public Function ChangePwd(ByVal oldPwd As String, ByVal newPwd As String)_
As Boolean
'...code...
Return True
End Function
7.<para>标记
如果文档没有使用各种内部的块级XML注释,如<list>和<code>,则在<summary>、<remarks>以及<returns>区域中添加的文本就会混合在一起。为了把它们划分为可读的段落,可以使用<para>标记。<para>标记中的文本会显示为一个独立的段落。其语法如下所示:
<para>This text will appear in a separate paragraph.</para>
8.<param>标记
为了说明函数声明中每个参数的用途,可以使用<param>标记。Visual Studio XML注释处理器会处理该标记。每个该标记都需要name特性,name特性的值与某个属性的名称相同。<param>起始标记和结束标记之间的内容是对参数的描述:
<param name="parameterName">Definition of parameter.</param>
XML处理器不允许为同一个参数创建多个<param>标记,或为不存在的参数创建<param>标记,否则Visual Studio会在Error List中写入警告信息。下面的代码使用<param>标记描述函数中的两个参数:
C#
/// <param name="oldPwd">Old password-must match the current password</param>
/// <param name="newPwd">New password-must meet the complexity requirements</param>
public bool ChangePwd(string oldPwd, string newPwd)
{
//...code...
return true;
}
VB
''' <param name="oldPwd">Old password-must match the current password</param>
''' <param name="newPwd">New password-must meet the complexity requirements</param>
Public Function ChangePwd(ByVal oldPwd As String, ByVal newPwd As String)_
As Boolean
'...code...
Return True
End Function
<param>标记非常适于记录方法参数的前提条件,如是否允许使用空值。
9.<paramref>标记
如果在文档的其他位置(不在<param>标记中)引用方法定义中的参数,就可以使用<paramref>标记来格式化值,或者根据XML转换的编码方式来链接参数信息。XML编译器不要求必须存在该参数的名称,但必须指定name特性要使用的文本,如下所示:
<paramref name="parameterName" />
通常,在文档大片内容中(如<summary>或者<remarks>标记中的文本)引用参数时,需要使用<paramref>标记,如下面的示例代码所示:
C#
/// <summary>
/// This function changes a user's password.This will throw an exception if
/// <paramref name="oldPwd" /> or <paramref name="newPwd" /> are nothing.
/// </summary>
/// <param name="oldPwd">Old password-must match the current password</param>
/// <param name="newPwd">New password-must meet the complexity requirements</param>
public bool ChangePwd(string oldPwd, string newPwd)
{
//...code...
return true;
}
VB
''' <summary>
''' This function changes a user's password.This will throw an exception if
''' <paramref name="oldPwd" /> or <paramref name="newPwd" /> are nothing.
''' </summary>
''' <param name="oldPwd">Old password-must match the current password</param>
''' <param name="newPwd">New password-must meet the complexity requirements</param>
Public Function ChangePwd(ByVal oldPwd As String, ByVal newPwd As String)_
As Boolean
'...code...
Return True
End Function
10.<permission>标记
要描述某个方法所需的代码访问安全权限集,可以使用<permission>标记。该标记使用cref特性来引用某个权限类型:
<permission cref="permissionName"> description goes here </permission>
如果函数需要多个权限,则可以使用多个<permission>块。下面是一个示例:
C#
/// <permission cref="System.Security.Permissions.RegistryPermission">
/// Needs full access to the Windows Registry.
/// </permission>
/// <permission cref="System.Security.Permissions.FileIOPermission">
/// Needs full access to the .config file containing application information./// </permission>
public string UserId { get; set; }
VB
''' <permission cref="System.Security.Permissions.RegistryPermission">
''' Needs full access to the Windows Registry.
''' </permission>
''' <permission cref="System.Security.Permissions.FileIOPermission">
''' Needs full access to the .config file containing application information.
''' </permission>
Public Property UserId()As String
11.<remarks>标记
<remarks>标记可以在文档中为某个方法添加一个注释块。在前面讨论其他标记时使用了<remarks>标记,其语法如下所示:
<remarks> Any further remarks go here </remarks>
一般情况下,可创建summary区域,简单地介绍方法或者类型,再在<remarks>标记中写入详细的信息,以及访问该成员的预期结果。
12.<returns>标记
方法把值返回给其调用代码时,可以使用<returns>标记描述返回的结果。<returns>的语法类似于其他块级标记,包括开始标记、结束标记,以及返回值的详细描述:
<returns> Description of the return value. </returns>
下面是<returns>标记在代码中的一个简单示例:
C#
/// <summary>
/// This function changes a user's password.
/// </summary>
/// <returns>
/// This function returns:
/// <c>True</c> which indicates that the password was changed successfully,
/// or <c>False</c> which indicates that the password change failed.
/// </returns>
public bool ChangePwd(string oldPwd, string newPwd)
{
//...code...
return true;
}
VB
''' <summary>
''' This function changes a user's password.
''' </summary>
''' <returns>
''' This function returns:
''' <c>True</c> which indicates that the password was changed successfully,
''' or <c>False</c> which indicates that the password change failed.
''' </returns>
Public Function ChangePwd(ByVal oldPwd As String, ByVal newPwd As String)_
As Boolean
'...code...
Return True
End Function
除了函数的返回值外,<returns>标记还可以注释需要的后置条件。
13.<see>标记
可使用<see>标记添加对项目中其他项的引用。与前面讨论的其他标记一样,<see>标记也需要cref特性,其值等于一个已存在的成员(属性、方法或类定义)。<see>标记可以嵌套在文档的其他标记中,如<summary>或者<remarks>。其语法如下所示:
<see cref="memberName" />
Visual Studio处理<see>标记时,会生成一个完全限定的地址,在通过样式表转换时,该地址可用作文档中的链接。例如,如果应用程序的类包含ChangePwd函数,那么cref值为:
<see cref="applicationName.className.ChangePwd"/>
下例使用<see>标记提供了对另一个函数CheckUser的链接:
C#
/// <remarks> /// Use <see cref="CheckUser" /> to verify that the user exists before calling /// ChangePwd. /// </remarks> public bool ChangePwd(string oldPwd, string newPwd) { //...code... return true; }
VB
''' <remarks> ''' Use <see cref="CheckUser" /> to verify that the user exists before calling ''' ChangePwd. ''' </remarks> Public Function ChangePwd(ByVal oldPwd As String, ByVal newPwd As String)_ As Boolean '...code... Return True End Function
仅在VB中,如果cref值指定的成员不存在,Visual Studio就会通过IntelliSense显示一条警告信息,并把它添加到Error List中。
14.<seeaIso>标记
<seealso>标记用于在一个独立的区域中列举出文档内的相关话题。与<see>内联在其他块中不同,<seealso>标记定义在其他XML注释块之外,每个<seealso>标记都需要在cref特性中指定要链接的属性、方法或类的名称。它的完整语法如下所示:
<seealso cref="memberName" />
修改前面的例子,下面是<seealso>标记的实现代码:
C#
/// <remarks>
/// Use <see cref="CheckUser" /> to verify that the user exists before calling
/// ChangePwd.
/// </remarks>
/// <seealso cref="ResetPwd" />
public bool ChangePwd(string oldPwd, string newPwd)
{
//...code...
return true;
}
VB
''' <remarks>
''' Use <see cref="CheckUser" /> to verify that the user exists before callin
''' ChangePwd.
''' </remarks>
''' <seealso cref="ResetPwd" />
Public Function ChangePwd(ByVal oldPwd As String, ByVal newPwd As String)_
As Boolean
'...code...
Return True
End Function
15.<summary>标记
<summary>标记用于在文档的相关话题顶部提供该话题的简单描述。它一般放在所有公有和受保护方法及类的前面。此外,在使用定制代码时,Visual Studio的IntelliSense引擎也会使用<summary>区域。<summary>标记的语法如下所示:
<summary> A description of the function or property goes here. </summary>
16.<typeparam>标记
在处理泛型或者成员定义时,<typeparam>标记提供了类型参数的信息。<typeparam>标记需要name特性,它指定了被引用的类型参数:
<typeparam name="typeName"> Description goes here. </typeparam>
C#或VB都可以使用<typeparam>标记,如下所示:
C#
/// <typeparam name="T">
/// Base item type (must implement IComparable)
/// </typeparam>
public class myList<T> where T : IComparable
{
//...code...
}
VB
''' <typeparam name="T">
''' Base item type (must implement IComparable)
''' </typeparam>
Public Class myList(Of T As IComparable)
'...code...
End Class
17.<typeparamref>标记
如果在文档的其他地方(不是<typeparam>标记)引用泛型类型参数,就可以使用<typeparamref>标记格式化其值,或者根据XML转换的编码方式链接到参数信息上。
<typeparamref name="parameterName" />
一般来说,在文档的大块区域(如<summary>或<remarks>标记)中引用参数时,使用<typeparamref>标记,如下面的代码所示:
C#
/// <summary>
/// Creates a new list of arbitrary type <typeparamref name="T"/>
/// </summary>
/// <typeparam name="T">
/// Base item type (must implement IComparable)
/// </typeparam>
public class myList<T> where T : IComparable
{
//...code...
}
VB
''' <summary>
''' Creates a new list of arbitrary type <typeparamref name="T"/>
''' </summary>
''' <typeparam name="T">
''' Base item type (must implement IComparable)
''' </typeparam>
Public Class myList(Of T As IComparable)
'...code...
End Class
18.<vaIue>标记
<value>标记通常用于定义属性的作用,它可以在XML文件中创建一个区域,提供相关成员的信息。IntelliSense不使用<value>标记。
<value>The text to display</value>
在与属性配合使用时,一般使用<summary>标记描述该属性的功能,而<value>标记用于描述该属性包含的内容。
C#
/// <summary>
/// The <c>UserId</c> property is used in conjunction with other properties
/// to set up a user properly.Remember to set the <c>Password</c> field too.
/// </summary>
/// <value>
/// A string containing the UserId for the current user
/// </value>
public string UserId { get; set; }
VB
''' <summary>
''' The <c>UserId</c> property is used in conjunction with other properties
''' to set up a user properly.Remember to set the <c>Password</c> field too.
''' </summary>
''' <value>
''' A string containing the UserId for the current user
''' </value>
Public Property UserId()As String