• C# XML 文档注释文件格式


    在编写 C# 代码时,只要在注释按照格式加入 XML 文档注释,例如:

    /// <summary>
    /// 这里是类的注释。
    /// </summary>
    public class MyClass { }
    

    就可以通过设置项目的"属性->生成->输出->XML 文档文件",来为当前项目生成包含所有文档注释的 XML 文件。一般可用于 Visual Studio 的智能提示,或者利用 Sandcastle 等工具生成文档。

    下面,我会介绍生成的 XML 文件的格式和相关规则,都以 C# 编译器生成的结果为基准。

    一、XML 文档注释文件格式

    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 则与成员本身的完全限定名类似,都是从命名空间的根开始,使用点分隔。不同的是:

    1. 成员名称中的点会被替换为 #,例如构造函数的名称 .ctor 会替换为 #ctor
    2. 由关键字指定的类型,会被替换为相应类型的完全限定名,例如 object 会替换为 System.Objectvoid 会替换为 System.Void
    3. 指针类型会表示为 *,引用类型会表示为 @
    4. 多维数组会表示为 [lowerbound:size,lowerbound:size],其中 lowerbound 是数组的指定维的下限,size 是相应的大小,未指定的话就直接省略。例如int[,] 会替换为 System.Int32[0:,0:]
    5. 泛型类型会省略掉泛型参数,并在类名后添加 `num,其中 num 是泛型参数的个数。例如 SampleType<T, T2> 会替换为 SampleType`2
    6. 如果成员中出现了对类型的泛型参数的引用,会使用 `idx 代替,其中 idx 是相应泛型参数在类型定义中的索引。例如上面的 SampleType<T, T2>,对 T的引用会替换为 `0,对 T2 的引用会替换为 `1
    7. 泛型方法同样会省略掉泛型参数,并在类名后添加 ``num,其中 num 是泛型参数的个数。例如 SampleType<T, T2>.SampleMethod<T3> 会替换为SampleType`2.SampleMethod``1
    8. 如果成员中出现了对方法的泛型参数的引用,会使用 ``idx 代替,其中 idx 是相应泛型参数在方法定义中的索引。例如上面的SampleType<T, T2>.SampleMethod<T3>,对 T3 的引用会替换为 ``0
    9. 泛型类型中的 < 和 > 会被替换成 { 和 },例如 IList<int> 会替换为 System.Collections.Generic.IList{System.Int32}
    10. 对于隐式和显式类型转换方法(op_Implicit 和 op_Explicit),由于往往单凭参数类型不足以唯一区分方法,因此会在方法后额外添加 ~returnType,其中 returnType 是方法的返回值。例如 operator SampleType(int x) 会替换为 SampleType.op_Explicit(System.Int32)~SampleType

    一个完整的实例如下所示,其中列出了每个成员对应的唯一标识符:

    using System.Collections.Generic;
    
    // Identifier is N:Cyjb
    namespace 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) { }
            }
        }
    }
  • 相关阅读:
    【剑指offer】24.反转链表
    【剑指offer】22. 链表中倒数第k个节点
    【每日一题-leetcode】84.柱状图中最大的矩形
    activity切换动画
    取消ActionBar的方法
    软工概论学习一
    软工概论学习二
    屏幕滑动监测以及触发事件
    Android 动画解释
    shape 学习
  • 原文地址:https://www.cnblogs.com/cyjb/p/XmlCodeDocumentationFormat.html
Copyright © 2020-2023  润新知