C# 代碼注釋規範文檔
C# 提供一種機制,使程式員可以使用含有 XML 文本的特殊注釋文法為他們的代碼編寫文檔。在源代碼檔案中,具有某種格式的注釋可用于指導某個工具根據這些注釋和它們後面的源代碼元素生成 XML。使用這類文法的注釋稱為文檔注釋 (documentation comment)。這些注釋後面必須緊跟使用者定義類型(如類、委托或接口)或者成員(如字段、事件、屬性或方法)。XML 生成工具稱作文檔生成器 (documentation generator)。(此生成器可以但不一定必須是 C# 編譯器本身。)由文檔生成器産生的輸出稱為文檔檔案 (documentation file)。文檔檔案可作為文檔檢視器 (documentation viewer) 的輸入;文檔檢視器是用于生成類型資訊及其關聯文檔的某種可視化顯示的工具。
此規範推薦了一組在文檔注釋中使用的标記,但是這些标記不是必須使用的,如果需要也可以使用其他标記,隻要遵循“符合格式标準的 XML”規則即可。
A.1. 介紹
具有特殊格式的注釋可用于指導某個工具根據這些注釋和它們後面的源代碼元素生成 XML。這類注釋是以三個斜杠 (///) 開始的單行注釋,或者是以一個斜杠和兩個星号 (/**) 開始的分隔注釋。這些注釋後面必須緊跟它們所注釋的使用者定義類型(如類、委托或接口)或者成員(如字段、事件、屬性或方法)。屬性節(第 17.2 節)被視為聲明的一部分,是以,文檔注釋必須位于應用到類型或成員的屬性之前。
文法:
single-line-doc-comment:
/// input-charactersopt
delimited-doc-comment:
/** delimited-comment-charactersopt */
在 single-line-doc-comment 中,如果目前 single-line-doc-comment 旁邊的每個 single-line-doc-comment 上的 /// 字元後跟有 whitespace 字元,則此 whitespace 字元不包括在XML 輸出中。
在 delimited-doc-comment 中,如果第二行上的第一個非 whitespace 字元是一個 asterisk,并且在 delimited-doc-comment 内的每行開頭都重複同一個由可選 whitespace 字元和 asterisk 字元組成的樣式,則該重複出現的樣式所含的字元不包括在 XML 輸出中。此樣式中,可以在 asterisk 字元之前或之後包括 whitespace 字元。
示例:
/// <summary>Class <c>Point</c> models a point in a two-dimensional
/// plane.</summary>
///
public class Point
{
/// <summary>method <c>draw</c> renders the point.</summary>
void draw() {…}
}
文檔注釋内的文本必須根據 XML 規則 (http://www.w3.org/TR/REC-xml) 設定正确的格式。如果 XML 不符合标準格式,将生成警告,并且文檔檔案将包含一條注釋,指出遇到錯誤。
盡管開發人員可自由建立它們自己的标記集,但第 A.2 節定義有建議的标記集。某些建議的标記具有特殊含義:
· <param> 标記用于描述參數。如果使用這樣的标記,文檔生成器必須驗證指定參數是否存在以及文檔注釋中是否描述了所有參數。如果此驗證失敗,文檔生成器将發出警告。
· cref 屬性可以附加到任意标記,以提供對代碼元素的參考。文檔生成器必須驗證此代碼元素是否存在。如果驗證失敗,文檔生成器将發出警告。查找在 cref 屬性中描述的名稱時,文檔生成器必須根據源代碼中出現的 using 語句來考慮命名空間的可見性。
· <summary> 标記旨在标出可由文檔檢視器顯示的有關類型或成員的額外資訊。
· <include> 标記表示應該包含的來自外部 XML 檔案的資訊。
注意,文檔檔案并不提供有關類型和成員的完整資訊(例如,它不包含任何關于類型的資訊)。若要獲得有關類型或成員的完整資訊,必須協同使用文檔檔案與對實際涉及的類型或成員的反射調用。
A.2. 建議的标記
文檔生成器必須接受和處理任何根據 XML 規則有效的标記。下列标記提供了使用者文檔中常用的功能。(當然,也可能有其他标記。)
标記 | 章節 | 用途 |
<c> | A.2.1 | 将文本設定為類似代碼的字型 |
<code> | A.2.2 | 将一行或多行源代碼或程式輸出設定為某種字型 |
<example> | A.2.3 | 表示所含的是示例 |
<exception> | A.2.4 | 辨別方法可能引發的異常 |
<include> | A.2.5 | 包括來自外部檔案的 XML |
<list> | A.2.6 | 建立清單或表 |
<para> | A.2.7 | 用于将結構添加到文本中 |
<param> | A.2.8 | 描述方法或構造函數的參數 |
<paramref> | A.2.9 | 确認某個單詞是參數名 |
<permission> | A.2.10 | 描述成員的安全性和通路權限 |
<summary> | A.2.11 | 描述一種類型 |
<returns> | A.2.12 | 描述方法的傳回值 |
<see> | A.2.13 | 指定連結 |
<seealso> | A.2.14 | 生成“請參見”項 |
<summary> | A.2.15 | 描述類型的成員 |
<value> | A.2.16 | 描述屬性 |
A.2.1. <c>
此标記提供一種機制以訓示用特殊字型(如用于代碼塊的字型)設定說明中的文本段落。對于實際代碼行,請使用 <code>(第 A.2.2 節)。
文法:
<c>text</c>
示例:
/// <summary>Class <c>Point</c> models a point in a two-dimensional
/// plane.</summary>
public class Point
{
// ...
}
A.2.2. <code>
此标記用于将一行或多行源代碼或程式輸出設定為某種特殊字型。對于叙述中較小的代碼段,請使用 <c>(第 A.2.1 節)。
文法:
<code>source code or program output</code>
示例:
/// <summary>This method changes the point\'s location by
/// the given x- and y-offsets.
/// <example>For example:
/// <code>
/// Point p = new Point(3,5);
/// p.Translate(-1,3);
/// </code>
/// results in <c>p</c>\'s having the value (2,8).
/// </example>
/// </summary>
public void Translate(int xor, int yor) {
X += xor;
Y += yor;
}
A.2.3. <example>
此标記用于在注釋中插入代碼示例,以說明如何使用所關聯的方法或其他庫成員。通常,此标記是同标記 <code>(第 A.2.2 節)一起使用的。
文法:
<example>description</example>
示例:
有關示例,請參見 <code>(第 A.2.2 節)。
A.2.4. <exception>
此标記提供一種方法以說明關聯的方法可能引發的異常。
文法:
<exception cref="member">description</exception>
其中
cref="member"
成員的名稱。文檔生成器檢查給定成員是否存在,并将 member 轉換為文檔檔案中的規範元素名稱。
description
對引發異常的情況的描述。
示例:
public class DataBaseOperations
{
/// <exception cref="MasterFileFormatCorruptException"></exception>
/// <exception cref="MasterFileLockedOpenException"></exception>
public static void ReadRecord(int flag) {
if (flag == 1)
throw new MasterFileFormatCorruptException();
else if (flag == 2)
throw new MasterFileLockedOpenException();
// …
}
}
A.2.5. <include>
此标記允許包含來自源代碼檔案外部的 XML 文檔的資訊。外部檔案必須是符合标準格式的 XML 文檔,還可以将 XPath 表達式應用于該文檔來指定應包含該 XML 文檔中的哪些 XML 文本。然後用從外部文檔中標明的 XML 來替換 <include>标記。
文法:
<include file="filename" path="xpath" />
其中
file="filename"
外部 XML 檔案的檔案名。該檔案名是相對于包含 include 标記的檔案進行解釋的(确定其完整路徑名)。
path="xpath"
XPath 表達式,用于選擇外部 XML 檔案中的某些 XML。
示例:
如果源代碼包含了如下聲明:
/// <include file="docs.xml" path=\'extradoc/class[@name="IntList"]/*\' />
public class IntList { … }
并且外部檔案“docs.xml”含有以下内容:
<?xml version="1.0"?>
<extradoc>
<class name="IntList">
<summary>
Contains a list of integers.
</summary>
</class>
<class name="StringList">
<summary>
Contains a list of integers.
</summary>
</class>
</extradoc>
這樣輸出的文檔就與源代碼中包含以下内容時一樣:
/// <summary>
/// Contains a list of integers.
/// </summary>
public class IntList { … }
A.2.6. <list>
此标記用于建立清單或項目表。它可以包含 <listheader> 塊以定義表或定義清單的标頭行。(定義表時,僅需要在标頭中為 term 提供一個項。)
清單中的每一項都用一個 <item> 塊來描述。建立定義清單時,必須同時指定 term 和 description。但是,對于表、項目符号清單或編号清單,僅需要指定 description。
文法:
<list type="bullet" | "number" | "table">
<listheader>
<term>term</term>
<description>description</description>
</listheader>
<item>
<term>term</term>
<description>description</description>
</item>
…
<item>
<term>term</term>
<description>description</description>
</item>
</list>
其中
term
要定義的術語,其定義位于 description 中。
description
是項目符号清單或編号清單中的項,或者是 term 的定義。
示例:
public class MyClass
{
/// <summary>Here is an example of a bulleted list:
/// <list type="bullet">
/// <item>
/// <description>Item 1.</description>
/// </item>
/// <item>
/// <description>Item 2.</description>
/// </item>
/// </list>
/// </summary>
public static void Main () {
// ...
}
}
A.2.7. <para>
此标記用于其他标記内,如 <summary>(第 A.2.11 節)或 <returns>(第 A.2.12 節),用于将結構添加到文本中。
文法:
<para>content</para>
其中
content
段落文本。
示例:
/// <summary>This is the entry point of the Point class testing program.
/// <para>This program tests each method and operator, and
/// is intended to be run after any non-trvial maintenance has
/// been performed on the Point class.</para></summary>
public static void Main() {
// ...
}
A.2.8. <param>
該标記用于描述方法、構造函數或索引器的參數。
文法:
<param name="name">description</param>
其中
name
參數名。
description
參數的描述。
示例:
/// <summary>This method changes the point\'s location to
/// the given coordinates.</summary>
/// <param name="xor">the new x-coordinate.</param>
/// <param name="yor">the new y-coordinate.</param>
public void Move(int xor, int yor) {
X = xor;
Y = yor;
}
A.2.9. <paramref>
該标記表示某單詞是一個參數。這樣,生成文檔檔案後經适當處理,可以用某種獨特的方法來格式化該參數。
文法:
<paramref name="name"/>
其中
name
參數名。
示例:
/// <summary>This constructor initializes the new Point to
/// (<paramref name="xor"/>,<paramref name="yor"/>).</summary>
/// <param name="xor">the new Point\'s x-coordinate.</param>
/// <param name="yor">the new Point\'s y-coordinate.</param>
public Point(int xor, int yor) {
X = xor;
Y = yor;
}
A.2.10. <permission>
該标記用于将成員的安全性和可通路性記入文檔。
文法:
<permission cref="member">description</permission>
其中
cref="member"
成員的名稱。文檔生成器檢查給定的代碼元素是否存在,并将 member 轉換為文檔檔案中的規範化元素名稱。
description
對成員的通路屬性的說明。
示例:
/// <permission cref="System.Security.PermissionSet">Everyone can
/// access this method.</permission>
public static void Test() {
// ...
}
A.2.11. <summary>
該标記用于指定類型的概述資訊。(使用 <summary>(第 A.2.15 節)描述類型的成員。)
文法:
<summary>description</summary>
其中
description
摘要文本。
示例:
/// <summary>Class <c>Point</c> models a point in a
/// two-dimensional plane.</summary>
public class Point
{
// ...
}
A.2.12. <returns>
該标記用于描述方法的傳回值。
文法:
<returns>description</returns>
其中
description
傳回值的說明。
示例:
/// <summary>Report a point\'s location as a string.</summary>
/// <returns>A string representing a point\'s location, in the form (x,y),
/// without any leading, trailing, or embedded whitespace.</returns>
public override string ToString() {
return "(" + X + "," + Y + ")";
}
A.2.13. <see>
該标記用于在文本内指定連結。使用 <seealso>(第 A.2.14 節)訓示将在“請參見”部分中出現的
文本。
文法:
<see cref="member"/>
其中
cref="member"
成員的名稱。文檔生成器檢查給定的代碼元素是否存在,并将 member 更改為所生成的文檔檔案中的元素名稱。
示例:
/// <summary>This method changes the point\'s location to
/// the given coordinates.</summary>
/// <see cref="Translate"/>
public void Move(int xor, int yor) {
X = xor;
Y = yor;
}
/// <summary>This method changes the point\'s location by
/// the given x- and y-offsets.
/// </summary>
/// <see cref="Move"/>
public void Translate(int xor, int yor) {
X += xor;
Y += yor;
}
A.2.14. <seealso>
該标記用于生成将列入“請參見”部分的項。使用 <see>(第 A.2.13 節)指定來自文本内的連結。
文法:
<seealso cref="member"/>
其中
cref="member"
成員的名稱。文檔生成器檢查給定的代碼元素是否存在,并将 member 更改為所生成的文檔檔案中的元素名稱。
示例:
/// <summary>This method determines whether two Points have the same
/// location.</summary>
/// <seealso cref="operator=="/>
/// <seealso cref="operator!="/>
public override bool Equals(object o) {
// ...
}
A.2.15. <summary>
可以用此标記描述類型的成員。使用 <summary>(第 A.2.11 節)描述類型本身。
文法:
<summary>description</summary>
其中
description
關于成員的摘要描述。
示例:
/// <summary>This constructor initializes the new Point to (0,0).</summary>
public Point() : this(0,0) {
}
A.2.16. <value>
該标記用于描述屬性。
文法:
<value>property description</value>
其中
property description
屬性的說明。
示例:
/// <value>Property <c>X</c> represents the point\'s x-coordinate.</value>
public int X
{
get { return x; }
set { x = value; }
}
A.3. 處理文檔檔案
文檔生成器為源代碼中每個附加了“文檔注釋标記”的代碼元素生成一個 ID 字元串。該 ID 字元串唯一地辨別源元素。文檔檢視器利用此 ID 字元串來辨別該文檔所描述的對應的中繼資料/反射項。
文檔檔案不是源代碼的階層化表現形式;而是為每個元素生成的 ID 字元串的一維清單。
A.3.1. ID 字元串格式
文檔生成器在生成 ID 字元串時遵循下列規則:
· 不在字元串中放置空白。
· 字元串的第一部分通過單個字元後跟一個冒号來辨別被辨別成員的種類。定義以下幾種成員:
字元 | 說明 |
E | 事件 |
F | 字段 |
M | 方法(包括構造函數、析構函數和運算符) |
N | 命名空間 |
P | 屬性(包括索引器) |
T | 類型(如類、委托、枚舉、接口和結構) |
! | 錯誤字元串;字元串的其他部分提供有關錯誤的資訊。例如,文檔生成器對無法解析的連結生成錯誤資訊。 |
· 字元串的第二部分是元素的完全限定名,從命名空間的根開始。元素的名稱、包含着它的類型和命名空間都以句點分隔。如果項名本身含有句點,則将用 # (U+0023) 字元替換。(這裡假定所有元素名中都沒有“# (U+0023)”字元。)
· 對于帶有參數的方法和屬性,接着是用括号括起來的參數清單。對于那些不帶參數的方法和屬性,則省略括号。多個參數以逗号分隔。每個參數的編碼都與 CLI 簽名相同,如下所示:參數由其完全限定名來表示。例如,int 變成 System.Int32、string 變成 System.String、object 變成 System.Object 等。具有 out 或 ref 修飾符的參數在其類型名後跟有 @ 符。對于由值傳遞或通過 params 傳遞的參數沒有特殊表示法。數組參數表示為 [ lowerbound : size , … , lowerbound : size ],其中逗号數量等于秩減去一,而下限和每個維的大小(如果已知)用十進制數表示。如果未指定下限或大小,它将被省略。如果省略了某個特定維的下限及大小,則“:”也将被省略。交錯數組由每個級别一個“[]”來表示。指針類型為非 void 的參數用類型名後面跟一個 * 的形式來表示。void 指針用類型名 System.Void 表示。
A.3.2. ID 字元串示例
下列各個示例分别示範一段 C# 代碼以及為每個可以含有文檔注釋的源元素生成的 ID 字元串:
· 類型用它們的完全限定名來表示。
enum Color { Red, Blue, Green }
namespace Acme
{
interface IProcess {...}
struct ValueType {...}
class Widget: IProcess
{
public class NestedClass {...}
public interface IMenuItem {...}
public delegate void Del(int i);
public enum Direction { North, South, East, West }
}
}
"T:Color"
"T:Acme.IProcess"
"T:Acme.ValueType"
"T:Acme.Widget"
"T:Acme.Widget.NestedClass"
"T:Acme.Widget.IMenuItem"
"T:Acme.Widget.Del"
"T:Acme.Widget.Direction"
· 字段用它們的完全限定名來表示。
namespace Acme
{
struct ValueType
{
private int total;
}
class Widget: IProcess
{
public class NestedClass
{
private int value;
}
private string message;
private static Color defaultColor;
private const double PI = 3.14159;
protected readonly double monthlyAverage;
private long[] array1;
private Widget[,] array2;
private unsafe int *pCount;
private unsafe float **ppValues;
}
}
"F:Acme.ValueType.total"
"F:Acme.Widget.NestedClass.value"
"F:Acme.Widget.message"
"F:Acme.Widget.defaultColor"
"F:Acme.Widget.PI"
"F:Acme.Widget.monthlyAverage"
"F:Acme.Widget.array1"
"F:Acme.Widget.array2"
"F:Acme.Widget.pCount"
"F:Acme.Widget.ppValues"
· 構造函數。
namespace Acme
{
class Widget: IProcess
{
static Widget() {...}
public Widget() {...}
public Widget(string s) {...}
}
}
"M:Acme.Widget.#cctor"
"M:Acme.Widget.#ctor"
"M:Acme.Widget.#ctor(System.String)"
· 析構函數。
namespace Acme
{
class Widget: IProcess
{
~Widget() {...}
}
}
"M:Acme.Widget.Finalize"
· 方法。
namespace Acme
{
struct ValueType
{
public void M(int i) {...}
}
class Widget: IProcess
{
public class NestedClass
{
public void M(int i) {...}
}
public static void M0() {...}
public void M1(char c, out float f, ref ValueType v) {...}
public void M2(short[] x1, int[,] x2, long[][] x3) {...}
public void M3(long[][] x3, Widget[][,,] x4) {...}
public unsafe void M4(char *pc, Color **pf) {...}
public unsafe void M5(void *pv, double *[][,] pd) {...}
public void M6(int i, params object[] args) {...}
}
}
"M:Acme.ValueType.M(System.Int32)"
"M:Acme.Widget.NestedClass.M(System.Int32)"
"M:Acme.Widget.M0"
"M:Acme.Widget.M1(System.Char,System.Single@,Acme.ValueType@)"
"M:Acme.Widget.M2(System.Int16[],System.Int32[0:,0:],System.Int64[][])"
"M:Acme.Widget.M3(System.Int64[][],Acme.Widget[0:,0:,0:][])"
"M:Acme.Widget.M4(System.Char*,Color**)"
"M:Acme.Widget.M5(System.Void*,System.Double*[0:,0:][])"
"M:Acme.Widget.M6(System.Int32,System.Object[])"
· 屬性和索引器。
namespace Acme
{
class Widget: IProcess
{
public int Width { get {...} set {...} }
public int this[int i] { get {...} set {...} }
public int this[string s, int i] { get {...} set {...} }
}
}
"P:Acme.Widget.Width"
"P:Acme.Widget.Item(System.Int32)"
"P:Acme.Widget.Item(System.String,System.Int32)"
· 事件。
namespace Acme
{
class Widget: IProcess
{
public event Del AnEvent;
}
}
"E:Acme.Widget.AnEvent"
· 一進制運算符。
namespace Acme
{
class Widget: IProcess
{
public static Widget operator+(Widget x) {...}
}
}
"M:Acme.Widget.op_UnaryPlus(Acme.Widget)"
下面列出可使用的一進制運算符函數名稱的完整集合:op_UnaryPlus、op_UnaryNegation、op_LogicalNot、op_OnesComplement、op_Increment、op_Decrement、op_True 和 op_False。
· 二進制運算符。
namespace Acme
{
class Widget: IProcess
{
public static Widget operator+(Widget x1, Widget x2) {...}
}
}
"M:Acme.Widget.op_Addition(Acme.Widget,Acme.Widget)"
下面列出可使用的二進制運算符函數名稱的完整集合:op_Addition、op_Subtraction、op_Multiply、op_Division、op_Modulus、op_BitwiseAnd、op_BitwiseOr、op_ExclusiveOr、op_LeftShift、op_RightShift、op_Equality、op_Inequality、op_LessThan、op_LessThanOrEqual、op_GreaterThan 和 op_GreaterThanOrEqual。
· 轉換運算符具有一個尾随“~”,然後再跟傳回類型。
namespace Acme
{
class Widget: IProcess
{
public static explicit operator int(Widget x) {...}
public static implicit operator long(Widget x) {...}
}
}
"M:Acme.Widget.op_Explicit(Acme.Widget)~System.Int32"
"M:Acme.Widget.op_Implicit(Acme.Widget)~System.Int64"
出處:http://www.cnblogs.com/cccc/archive/2009/08/19/1549866.html