在編寫 C# 代碼時,只要在注釋按照格式加入 XML 文檔注釋,例如:
/// <summary>/// 這里是類的注釋。/// </summary>public class MyClass { }
就可以通過設置項目的"屬性->生成->輸出->XML 文檔文件",來為當前項目生成包含所有文檔注釋的 XML 文件。一般可用于 Visual Studio 的智能提示,或者利用Sandcastle等工具生成文檔。
下面,我會介紹生成的 XML 文件的格式和相關規則,都以 C# 編譯器生成的結果為基準。
XML 文檔注釋的文件格式非常簡單,就是一個包含了所有注釋的列表,一個簡單的例子如下所示:
XML 文件的根節點是doc
,下面包含兩個子節點assembly
和members
。其中assembly
是 XML 文件對應的程序集名稱,members
則包含了多個member
節點,列出了所有的注釋(不區分是公共、受保護的還是私有成員)。member
節點的name
元素是一個唯一的標識符,與程序集中定義的類、方法、屬性、字段等成員一一對應。在編寫文檔注釋時指定的cref
屬性,也會全部轉換為標識符,而不是原先指定的成員名稱。
<?xml version="1.0"?><doc> <assembly> <name>Cyjb</name> </assembly> <members> <member name="T:Cyjb.ArrayExt"> <summary> 提供數組的擴展方法。 </summary> </member> <member name="M:Cyjb.ArrayExt.Left``1(``0[],System.Int32)"> <summary> 從當前數組的左端截取一部分。 </summary> <typeparam name="T">數組中元素的類型。</typeparam> <param name="array">從該數組返回其最左端截取的部分。</param> <param name="length">要截取的元素個數。 如果為 <c>0</c>,則返回空數組。如果大于或等于 <paramref name="array"/> 的長度, 則返回整個數組的一個淺拷貝。</param> <returns>從指定數組的左端截取的部分。</returns> <exception cref="T:System.ArgumentNullException"><paramref name="array"/> 為 <c>null</c>。</exception> <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="length"/> 小于 <c>0</c>。</exception> </member> ... </members></doc>
二、唯一標識符規則
唯一標識符總是Type:FullName
的格式,其中Type
表示對應成員的類型,FullName
是對應成員的完全限定名,中間是用:
分隔。
成員類型Type
的可能值有:
N
- 命名空間。T
- 類型,包括類、接口、結構體、枚舉和委托。F
- 字段。P
- 屬性。M
- 方法,包括普通方法、構造函數和運算符重載。E
- 事件。!
- 錯誤成員,一般是由于編譯器無法識別指定的成員類型,例如<see cref="MemberNotExists"/>
,就會被編譯器轉換為<see cref="!:MemberNotExists"/>
。完全限定名FullName
則與成員本身的完全限定名類似,都是從命名空間的根開始,使用點分隔。不同的是:
#
,例如構造函數的名稱.ctor
會替換為#ctor
。object
會替換為System.Object
,void
會替換為System.Void
。*
,引用類型會表示為@
[lowerbound:size,lowerbound:size]
,其中lowerbound
是數組的指定維的下限,size
是相應的大小,未指定的話就直接省略。例如int[,]
會替換為System.Int32[0:,0:]
。`num
,其中num
是泛型參數的個數。例如SampleType<T, T2>
會替換為SampleType`2
。`idx
代替,其中idx
是相應泛型參數在類型定義中的索引。例如上面的SampleType<T, T2>
,對T
的引用會替換為`0
,對T2
的引用會替換為`1
。``num
,其中num
是泛型參數的個數。例如SampleType<T, T2>.SampleMethod<T3>
會替換為SampleType`2.SampleMethod``1
。``idx
代替,其中idx
是相應泛型參數在方法定義中的索引。例如上面的SampleType<T, T2>.SampleMethod<T3>
,對T3
的引用會替換為``0
。<
和>
會被替換成{
和}
,例如IList<int>
會替換為System.Collections.Generic.IList{System.Int32}
。op_Implicit
和op_Explicit
),由于往往單憑參數類型不足以唯一區分方法,因此會在方法后額外添加~returnType
,其中returnType
是方法的返回值。例如Operator SampleType(int x)
會替換為SampleType.op_Explicit(System.Int32)~SampleType
。一個完整的實例如下所示,其中列出了每個成員對應的唯一標識符:
using System.Collections.Generic;// Identifier is N:Cyjbnamespace Cyjb{ /// <summary> /// Identifier is T:Cyjb.SampleType /// </summary> public unsafe class SampleType { /// <summary> /// Identifier is F:Cyjb.SampleType.SampleValue /// </summary> public const int SampleValue = 0; /// <summary> /// Identifier is F:Cyjb.SampleType.SampleValue2 /// </summary> public int SampleValue2 = 0; /// <summary> /// Identifier is M:Cyjb.SampleType.#ctor /// </summary> public SampleType() { } /// <summary> /// Identifier is M:Cyjb.SampleType.#ctor(System.Int32) /// </summary> public SampleType(int value) { } /// <summary> /// Identifier is M:Cyjb.SampleType.SampleMethod /// </summary> public void SampleMethod() { } /// <summary> /// Identifier is M:Cyjb.SampleType.SampleMethod(System.Int32,System.Int32@,System.Int32*) /// </summary> public void SampleMethod(int a, ref int b, int* c) { } /// <summary> /// Identifier is M:Cyjb.SampleType.SampleMethod(System.Int32[],System.Int32[0:,0:],System.Int32[][]) /// </summary> public void SampleMethod(int[] a, int[,] b, int[][] c) { } /// <summary> /// Identifier is M:Cyjb.SampleType.SampleMethod``1(``0,``0[],System.Collections.Generic.IList{``0},System.Collections.Generic.IList{System.Collections.Generic.IList{``0[]}}) /// </summary> public void SampleMethod<T>(T a, T[] b, IList<T> c, IList<IList<T[]>> d) { } /// <summary> /// Identifier is M:Cyjb.SampleType.op_Addition(Cyjb.SampleType,Cyjb.SampleType) /// </summary> public static SampleType operator +(SampleType x, SampleType y) { return null; } /// <summary> /// Identifier is M:Cyjb.SampleType.op_Explicit(System.Int32)~Cyjb.SampleType /// </summary> public static explicit operator SampleType(int x) { return null; } /// <summary> /// Identifier is M:Cyjb.SampleType.op_Implicit(Cyjb.SampleType)~System.Int32 /// </summary> public static implicit operator int(SampleType x) { return 0; } /// <summary> /// Identifier is P:Cyjb.SampleType.SamplePRoperty /// </summary> public int SampleProperty { get; set; } /// <summary> /// Identifier is P:Cyjb.SampleType.Item(System.Int32) /// </summary> public int this[int index] { get { return 0; } } /// <summary> /// Identifier is T:Cyjb.SampleType.SampleDelegate /// </summary> public delegate void SampleDelegate(int a); /// <summary> /// Identifier is E:Cyjb.SampleType.SampleEvent /// </summary> public event SampleDelegate SampleEvent; /// <summary> /// Identifier is T:Cyjb.SampleType.NestedType /// </summary> public class NestedType { } /// <summary> /// Identifier is T:Cyjb.SampleType.NestedType2`1 /// </summary> public class NestedType2<T> { /// <summary> /// Identifier is M:Cyjb.SampleType.NestedType2`1.TestMethod``1(`0,``0,System.Collections.Generic.IDictionary{`0,``0}) /// </summary> public void TestMethod<T2>(T a, T2 b, IDictionary<T, T2> c) { } } }}
新聞熱點
疑難解答