Комментарии XML-документации

В исходных файлах C# могут находиться структурированные комментарии, используемые для создания документации по API для типов, определенных в этих файлах. Компилятор C# создает файл XML, содержащий структурированные данные, представляющие комментарии и сигнатуры API. Другие средства могут обрабатывать эти выходные данные в формате XML и создавать удобочитаемую документацию, например, в виде веб-страниц или PDF-файлов.

Благодаря этому процессу добавление документации по API в код предоставляет множество преимуществ:

  • Компилятор C# объединяет структуру кода C# и текст комментариев в XML-документ.
  • Компилятор C# проверяет, соответствуют ли комментарии сигнатурам API для соответствующих тегов.
  • Средства, обрабатывающие XML-файлы документации, могут определять XML-элементы и атрибуты, характерные для этих средств.

Такие средства как Visual Studio предоставляют IntelliSense для многих распространенных XML-элементов, используемых в комментариях к документации.

В этой статье рассматриваются следующие темы:

  • Комментарии к документации и создание XML-файла
  • Теги, проверяемые компилятором C# и Visual Studio
  • Формат созданного XML-файла

Создание выходных данных XML-документации

Вы создаете документацию для кода, указывая ее в специальных полях комментариев, обозначаемых тройными косыми чертами. Поля комментария включают XML-элементы, которые описывают блок кода, следующий за комментариями. Пример:

/// <summary>
///  This class performs an important function.
/// </summary>
public class MyClass {}

Вы задаете параметр DocumentationFile или DocumentationFile, а компилятор находит все поля комментариев с XML-тегами в исходном коде и создает XML-файл документации на основе этих комментариев. Если этот параметр включен, компилятор создаст предупреждение CS1591 для любого открытого видимого члена, объявленного в проекте без комментариев XML-документации.

Формат комментариев 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>
      */
      
    • Компилятор не обнаруживает шаблон в следующем комментарии по двум причинам. Во-первых, количество пробелов перед звездочкой не является одинаковым в разных строках. Во-вторых, пятая строка начинается с символа табуляции, который не является пробелом. Весь текст из строк со второй по пятую обрабатывается как часть комментария.

      /**
        * <summary>
        * text
      *  text2
       	*  </summary>
      */
      

Для ссылки на XML-элементы (например, если функция обрабатывает определенные XML-элементы, которые требуется включить в комментарии XML-документации) можно использовать стандартный механизм заключения в скобки (&lt; и &gt;). Для ссылки на универсальные идентификаторы в элементах ссылок кода (cref) можно использовать escape-символы (например, cref="List&lt;T&gt;") или фигурные скобки (cref="List{T}"). В особом случае компилятор анализирует фигурные скобки, как угловые, чтобы при ссылке на универсальные идентификаторы сделать комментарий документации менее громоздким.

Примечание

Комментарии XML-документации не являются метаданными. Они не включаются в скомпилированную сборку, и поэтому не доступны посредством отражения.

Средства, принимающие входные данные XML-документации

Следующие средства создают выходные данные на основе XML-комментариев:

  • DocFX: DocFX — это генератор документации API для .NET, который сейчас поддерживает C#, Visual Basic и F#. Он также позволяет изменять создаваемую справочную документацию. DocFX создает статический веб-сайт HTML на основе исходного кода и файлов Markdown. Кроме того, DocFX предоставляет гибкие возможности по настройке макета и стиля веб-сайта с помощью шаблонов. Можно также создавать пользовательские шаблоны.
  • Sandcastle: средства Sandcastle создают файлы справки для библиотек управляемых классов, содержащие как концептуальные страницы справки, так и страницы справки для API. Средства Sandcastle работают из командной строки и не имеют графического интерфейса, функций управления проектами или автоматизированного процесса сборки. Средство Sandcastle Help File Builder предоставляет автономный пользовательский интерфейс и инструменты командной строки для создания файла справки в автоматическом режиме. Также доступен пакет интеграции Visual Studio, позволяющий создавать проекты справки и управлять ими только из Visual Studio.
  • Doxygen: Doxygen создает интерактивный обозреватель документации (в формате HTML) или автономное справочное руководство (в формате LaTeX) на основе набора исходных файлов с комментариями документации. Также поддерживается создание документации в форматах RTF (MS Word), PostScript, PDF с гиперссылками, HTML со сжатием, DocBook и страницы руководства Unix. Doxygen можно настроить для извлечения структуры кода из исходных файлов без комментариев документации.

Строки идентификаторов

Каждый тип или член хранится в элементе в выходном XML-файле. Каждый из этих элементов имеет уникальную строку идентификатора, определяющую тип или член. Строка идентификатора должна учитывать операторы, параметры, возвращаемые значения, параметры универсального типа, а также параметры ref, in и out. Чтобы закодировать все эти потенциальные элементы, компилятор следует четко определенным правилам при создании строк идентификаторов. С помощью строки идентификатора программы, обрабатывающие XML-файл, могут определить, к какому элементу метаданных или отражения .NET применяется эта документация.

Компилятор соблюдает следующие правила при формировании строк идентификаторов.

  • В строке нет пробелов.

  • Первая часть строки определяет тип элемента, использующего один символ, за которым следует двоеточие. Используются следующие типы элементов.

    Знак Тип члена Примечания
    Нет namespace Вы не можете присвоить комментарий документации пространству имен, но можете создать для него cref-ссылку, если такие ссылки поддерживаются.
    T type В качестве типа может использоваться класс, интерфейс, структура, перечисление или делегат.
    C поле
    С свойство; Включает индексаторы или другие индексированные свойства.
    M method Включает специальные методы, такие как конструкторы и операторы.
    E event
    ! текст ошибки Остальная часть строки предоставляет сведения об ошибке. Компилятор C# создает сведения об ошибках для ссылок, которые не могут быть разрешены.
  • Вторая часть строки содержит полное имя элемента, начиная от корня пространства имен. Имя элемента, включающие типы и пространства имен разделяются точками. Если в имени самого элемента есть точки, они заменяются символами решетки ("#"). Предполагается, что в именах элементов не может содержаться символ решетки. Например, полное имя конструктора для объекта String имеет вид System.String.#ctor.

  • Для свойств и методов указывается список параметров, заключенный в круглые скобки. Если параметры не используются, скобки отсутствуют. Параметры разделяются запятыми. Кодирование каждого параметра прямо соответствует тому, как он кодируется в сигнатуре .NET (определения всех элементов в следующем списке, записанных заглавными буквами, см. в разделе Microsoft.VisualStudio.CorDebugInterop.CorElementType):

    • Базовые типы. Обычные типы (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 представляется как [ELEMENT_TYPE_ARRAY: size ,size: size ], где число запятых равно рангу 1, а нижние границы и размер каждого измерения, если они известны, представлены в десятичном формате. Если нижняя граница или размер не указаны, они опускаются. Если нижняя граница и размер для определенной размерности опущены, опускается и символ ":". Например, двухмерный массив со значением 1 для нижних границ без указания размеров обозначается так: "[1:,1:]".
  • Только для операторов преобразования (op_Implicit и op_Explicit) возвращаемое значение метода кодируется как символ ~, за которым следует тип возвращаемого значения. Например: <member name="M:System.Decimal.op_Explicit(System.Decimal arg)~System.Int32"> — это тег для оператора приведения public static explicit operator int (decimal value);, объявленного в классе System.Decimal.

  • Для универсальных типов в конце имени указывается символ обратного апострофа и число, которое обозначает количество параметров универсального типа. Например: <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 (ELEMENT_TYPE_FNPTR)", где type — тип возвращаемого значения, а type — это аргументы метода. Если аргументы не используются, скобки опускаются. Компилятор C# никогда не создает такую кодировку.
    • Следующие компоненты сигнатуры не представляются, поскольку они никогда не используются для различения перегруженных методов:
      • соглашение о вызовах;
      • тип возвращаемого значения;
      • ELEMENT_TYPE_SENTINEL

Примеры ниже демонстрируют, как создаются строки идентификаторов для класса и его элементов.

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() { return 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 nm, 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] { get { return 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) { return 1; }
    }
}

Спецификация языка C#

Дополнительные сведения см. в приложении Спецификация языка C# в комментариях документации.