XML-комментарии в C#

Вероятно вы слышали об особом виде комментариев в Visual Stuidio – XML-комментарии. Поговорим о них подробнее и разберемся, зачем они нужны программисту.

Если коротко, то C# позволяет разработчикам встраивать XML-комментарии в исходный код, а затем с их помощью создавать документацию прямо из исходных файлов Visual Studio. Это очень полезная функциональность, особенно когда над проектом работают несколько программистов. Синтаксический анализатор (parser) C# может раскрывать такие XML-тэги, предоставляя дополнительную информацию, и экспортировать их во внешний документ для дальнейшей обработки.

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

Сама возможность хранить информацию о коде в самом коде – это очень удобно. Подобное решение облегчает изменение документации одновременно с исходным кодом. Ведь разбираться в чужом коде — задача неблагодарная даже для опытных программистов. Что уж говорить о тех, кто не слишком подкован в программировании.

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

Теория

Каждый XML-комментарий начинается с трех символов «///». Первые два обозначают обычный комментарий и указывают компилятору игнорировать дальнейший текст, а третий сообщает синтаксическому анализатору, что это XML-комментарий, который следует обрабатывать по-особому.

Когда разработчик набирает три символа «///» подряд, интегрированная среда разработки (IDE) Visual Studio проверяет, не предшествуют ли они распознаваемому типу или определению элемента типа. Если да, IDE автоматически вставляет некоторые тэги. После этого разработчик может добавить другие нужные ему тэги и значения.

Давайте проверим это на практике. Создадим какую-нибудь функцию с параметром. Например, так.


private void ShowMessage(string msg)
{
    MessageBox.Show(msg);
}

Незамысловатый код, который выводит обычное окно с сообщением. Теперь перейдите на строчку выше описанной функции и поставьте подряд три символа «///». У вас сразу сгенерируется заготовка, которая будет выглядеть следующим образом.


/// <summary>
/// 
/// </summary>
/// <param name="msg"></param>
private void ShowMessage(string msg)
{
    MessageBox.Show(msg);
}

Как видите, у вас появились XML-тэги <summary> и <param>. Таких тэгов гораздо больше. Стоит заметить, что текущая реализация IntelliSense в Visual Studio распознает не все перечисленные в спецификации C# тэги, но недостающие тэги всегда можно вставить вручную.

После того, как вы расставите все комментарии в своем коде, неплохо бы что-нибудь с ними сделать. Например, экспортировать во внешний файл всего текста, расположенного за тремя символами «///».

Есть несколько предопределенных XML-тэгов, известных синтаксическому анализатору. Обнаруживая эти тэги (при условии, что разработчик правильно ими воспользовался), он форматирует их текст и выводит в текстовый файл. Если встречается тэг, отличный от предопределенного, он и его текст без изменений включаются во внешний файл. Благодаря этому вы можете создавать собственные тэги или даже использовать известные тэги из других источников (например, HTML).

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

Для этого откройте свойства проекта (Project | Имя проекта Properties) и в появившемся диалоговом окне выберите вкладку Build. В правой секции окна вы увидите поле XML Documentation File. Укажите в нем путь (относительно каталога проекта) и имя нужного файла.

В данном случае в качестве имени документа использовалось имя, предложенное Visual Studio. Если вы не укажете здесь путь и имя файла (вариант по умолчанию), XML-комментарии не будут извлекаться в отдельный файл.

Распознаваемые тэги

Можно разделить тэги XML-комментариев на две категории. В первую входят первичные тэги (primary tags); с них всегда начинается какая-либо группа тэгов XML-комментариев. Первичные тэги никогда не вкладываются в другие тэги. Ко второй категории относятся тэги, используемые совместно с первичными в качестве модификаторов текста. Назовем их вспомогательными (support tags). Чтобы различать вспомогательные и HTML-тэги, первые набираются в нижнем регистре, вторые — в верхнем. Примеры первичных и вспомогательных тэгов, которые будут рассмотрены в статье, приведены в следующем листинге.

Листинг 1. Фрагмент файла GiveHelpTransforms.cs


namespace GiveHelp
{
/// <remarks>
/// Class that contains functions to do transformations to help 
/// files. The source XML is loaded into the <see cref="SourceXML"/> 
/// property (e.g. <c><I>obj</I>.SourceXML = "<I>XML goes 
/// here</I>"</c>). One of the associated member functions 
/// (<see cref="GiveTypeListHTMLHelp"/>,
/// <see cref="GiveMemberListHTMLHelp"/>,
/// <see cref="GiveMemberHTMLHelp"/>)
/// is called to initiate and then return the transformation.
/// <para>
/// <list type="table">
/// <listheader>
/// <term>Help Page</term>
/// <description>Function to call</description>
/// </listheader>
/// <item><term>List of Types</term>
/// <description>GiveTypeListHTMLHelp</description></item>
/// <item><term>List of members</term>
/// <description>GiveMemberListHTMLHelp</description></item>
/// <item><term>Help for a single member</term>
/// <description>GiveMemberHTMLHelp</description></item>
/// </list>
/// </para>
/// </remarks>
/// <permission cref="">public</permission>
/// <example><code>
/// // Создать класс, осуществляющий преобразования
/// GiveHelpTransforms ght = new GiveHelpTransforms();
/// // Загрузить XML в свойство SourceXML
/// ght.LoadXMLFromFile(
/// "E:\\Inetpub\\wwwroot\\GiveHelp\\GiveHelpDoc.xml");
///
/// // Выполнить преобразование и вывести строку
/// Response.Write( ght.
/// GiveMemberHTMLHelp(Request.QueryString.Get("Type"),
/// Request.QueryString.Get("Member")) );
/// </code></example>
public class GiveHelpTransforms
{
...
/// <summary>
/// Calling this function will take the XML in <see
/// cref="SourceXML"/>

Существует 9 первичных тэгов: <remarks>, <summary>, <example>, <exception>, <param>, <permission>, <returns>, <seealso> и <include>. В этом контексте тэг <remarks> используется для описания какого-либо типа, например класса:


/// <remarks>
/// Класс, содержащий функции для преобразований в справочных файлах
/// </remarks>

В документации по языку C# рекомендуется применять тэг <remarks> для описания типа, а тэг <summary> — для описания элемента типа. Однако, если перед типом ввести комбинацию «///», Visual Studio IDE вставит тэг <summary>. Следовательно, тэг <remarks> надо вводить вручную.

Тэг <summary>

Из всех тэгов в файлах исходного кода на C# чаще всего встречается тэг <summary>. Он описывает элементы (члены) типа, включая методы, свойства и поля:


/// <summary>
/// Этот атрибут, основанный на XmlDocument, содержит
/// XML-документацию по проекту
/// </summary>

С помощью тэга <summary> можно описывать и сами типы, но это не рекомендуется. Как уже говорилось выше для типов нужно использовать тэг <remarks>.

Другие тэги

Тэг <example> выделяет пример использования элемента. Примером может быть любой текст, в том числе фрагмент кода:


/// <example>
/// <code>
/// // Создать класс для преобразований
/// GiveHelpTransforms ght = new GiveHelpTransforms();
/// // Загрузить XML в свойство SourceXML
/// ght.LoadXMLFromFile(
///    "E:\\Inetpub\\wwwroot\\GiveHelp\\GiveHelpDoc.xml");
/// </code>
/// </example>

Код обычно помечается тэгом <code> (о нем — в разделе по вспомогательным тэгам).

Тэг <exception> документирует исключения, которые может генерировать элемент. Для описания нескольких исключений можно использовать несколько таких тэгов. В отличие от большинства других тэгов, <exception> имеет один атрибут — cref. Его значение — имя возбуждаемого исключения.

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

В коде, приведенном ниже, никакие исключения не генерируются, но я все же рассмотрим применение атрибута cref в тэге <exception>


/// <exception cref="SampleException">
/// Обычно здесь описывается исключение
/// </exception>

Тэг <param> описывает параметры метода или свойства. Он автоматически вставляется IDE, если перед методом указываются три символа «///». Тэг <param> имеет один атрибут — name — совпадающий с именем параметра:


/// <param name="strFilePath">Путь к файлу, содержащему
/// исходный XML</param>

Тэг <permission>

Разрешение на доступ к элементам указывается в тэге <permission>. Соответствующий ему текст содержит описание разрешения. К значению этого тэга не предъявляется никаких требований. Это может быть информация о виде разрешения на доступ: является ли элемент открытым (public), закрытым (private), защищенным (protected) и др. Но значением может быть и область видимости (scope) с информацией о том, статический ли это метод. В общем, можете включать туда все, что сочтете нужным.

У тэга <permission> имеется единственный атрибут — cref. В документации атрибут cref в этом контексте описывается как «ссылка на элемент или поле, доступное из текущей среды компиляции». Его обычно устанавливают как System.Security.PermissionSet:


/// <permission cref="System.Security.PermissionSet">Открытый
/// доступ</permission>

Тэг <returns>

Тэг <returns> похож на тэг <param>, но описывает возвращаемое значение метода или свойства:


/// <returns>HTMLкод, содержащий список типов из XML
/// документации.</returns>

Тэг <seealso>

В тэге <seealso> указываются «прочие» ссылки по этому тематическому разделу (topic). Этот тэг обычно не содержит текстовое значение — только атрибут cref со ссылкой на нужный символ (тип, элемент, поле и т. д.).


/// <seealso cref="GiveMemberListHTMLHelp"/>

Компилятор XML определяет содержимое по указанному символу и соответственно использует его при компиляции XML-документации.

Тэг <include>

Когда компилятор извлекает XML-комментарии из кода, любой файл, указанный в тэге <include>, раскрывается, и содержащиеся в нем комментарии интерпретируются так, будто они находятся в самом исходном коде. Благодаря этому комментарии можно хранить во внешнем файле. Правда, тогда их не будет «под рукой», и лично я стараюсь не пользоваться тэгом <include>. Но если у вас очень длинные комментарии, такой вариант, вероятно, вас устроит.

В тэге <include> должно присутствовать несколько атрибутов, характеризующих внешний файл. Атрибут file задает имя файла, причем путь может быть относительным или полным. Включаемый файл представляет собой XML-документ с XML-комментариями. Атрибут path — это выражение XPath, указывающее на родительский элемент XML-комментария во внешнем документе:


/// <include file='MyXMLCommentFile.xml'
/// path='doc/members/member[@name="T:MyExampleClass"]/*'/>
public class MyExampleClass
{
    /// <include file='MyXMLCommentFile.xml'
    /// path='doc/members/member[@name="M:MyExampleMethod"]/*'/>
    public string MyExampleMethod(string strReturnThis)
    {
     return strReturnThis;
    }
}

Ниже приведен текст файла MyXMLCommentFile.xml. Заметьте, что в этом примере я указал родительский элемент, содержащий XMLкомментарии, с помощью XPath. Реальный формат файла выбирается самим разработчиком. Я предпочел формат, аналогичный формату файлов XML-документов, генерируемых компилятором.


<doc>
<members>
<member name="T:MyExampleClass">
<remarks>
This is my example class that does <b>nothing</b>.
</remarks>
</member>
<member name="M:MyExampleMethod">
<summary>
This method just returns a string.
</summary>
<param name="strReturnThis">
This is a string parameter that will just be returned.
</param>
<returns>
Just returns the input parameter.
</returns>
</member>
</members>
</doc>

Вспомогательные тэги

Существует 11 вспомогательных тэгов: <c>, <code>, <list>, <listheader>, <item>, <term>, <description>, <para>, <paramref>, <see> и <value>.

Тэг <c> определяет строку текста как код и часто встречается в текстах описаний:


/// Исходный XML загружается в свойство <see cref="SourceXML"/>
/// (например, <c><I>obj</I>.SourceXML = "<I>Сюда помещается 
/// XML</I>"</c>)

Тэг <code> определяет раздел текста как блок кода. Он часто используется внутри блока тэга <example> (вы уже видели это), указывая, что формат текста в комментариях должен быть сохранен:


/// <code>
/// // Создать класс, осуществляющий преобразования
/// GiveHelpTransforms ght = new GiveHelpTransforms();
/// // Загрузить XML в свойство SourceXML
/// ght.LoadXMLFromFile(
///    "E:\\Inetpub\\wwwroot\\GiveHelp\\GiveHelpDoc.xml");
/// </code>

Тэг <list> применяется внутри других тэгов комментариев и определяет специализированный список (specialized list). Тип списка задается атрибутом, значением которого может быть number (нумерованный), bullet (маркированный) или table (таблица). Внутри тэга <list> размещаются тэги, обозначающие компоненты списка, например:


/// <list type="table">
/// <listheader>
/// <term>Help Page</term>
/// <description>Function to call</description>
/// </listheader>
/// <item><term>List of types</term>
/// <description>GiveTypeListHTMLHelp</description></item>
/// <item><term>List of members</term>
/// <description>GiveMemberListHTMLHelp</description></item>
/// <item><term>Help for a single member</term>
/// <description>GiveMemberHTMLHelp</description></item>
/// </list>

Тэг <listheader> содержит информацию о заголовке списка (см. предыдущий пример). Он обычно используется для списков табличного типа.

Тэг <item>, как вы, наверное, уже догадались, обозначает элемент в списке. Он может включать в себя тэги <term> и <description>. Последние всегда являются дочерними по отношению к тэгу <listheader> или <item>, используются парно, и их назначение очевидно.

Тэг <para> идентифицирует новый абзац и очень похож на тэг <p> в HTML. Понятно, что он предназначен для разбиения длинных комментариев на абзацы:


/// <summary>Это аннотация.
/// <para>Это новый абзац.</para>
/// </summary>

Если в тексте комментария надо выделить некий параметр, используется тэг <paramref>. Он помещается там, где в тексте должно выводиться имя параметра. Основное назначение этого тэга — особым образом выделять имя параметра. У него один атрибут — name, который представляет собой имя параметра, выводимого в тексте в особом формате:


/// Загружает XML-документацию в файл, заданный
/// <paramref>strFilePath</paramref>

Тэг <see> употребляется в тексте других тэгов комментарииев для обозначения гиперссылки. Он вставляется как часть текста и обычно включает один атрибут, cref:


/// Чтобы выполнить преобразование и вернуть результат,
/// вызывается одна из функций-членов
/// (<see cref="GiveTypeListHTMLHelp"/>,
/// <see cref="GiveMemberListHTMLHelp"/>,
/// <see cref="GiveMemberHTMLHelp"/>)

Атрибут cref — ссылка на существующий символ. Более подробную информацию см. в описании тэга <description>.

Тэг <value> определяет значение какого-либо свойства. Он аналогичен тэгу <remarks>, описывающему классы, и тэгу <summary>, описывающему остальные элементы.


/// <value>
/// Свойство SourceXML содержит XML, используемый
/// функциямичленами этого класса при преобразованиях.
/// </value>

Файл XML-документа

Выше уже говорилось, как настроить проект для экспорта XML-комментариев. В следующем листинге приведен фрагмент файла XML-документа, созданного после компиляции моего проекта-примера.

Листинг 2. Фрагмент файла GiveHelpDoc.xml


<?xml version="1.0"?>
<doc>
<assembly>
<name>GiveHelp</name>
</assembly>
<members>
<member name="T:GiveHelp.GiveHelpTransforms">
<remarks>
Class that contains functions to do transformations to help
files. The source XML is loaded into the <see
cref="P:GiveHelp.GiveHelpTransforms.SourceXML"/> property
(e.g. <c><I>obj</I>.SourceXML = "<I>XML goes here</I>"
</c>). One of the associated member functions (<see
cref="M:GiveHelp.GiveHelpTransforms.
GiveMemberListHTMLHelp(System.String)"/>, <see
cref="M:GiveHelp.GiveHelpTransforms.GiveMemberHTMLHelp
(System.String)"/>) is called to initiate and then return
the transformation.
<para>
<list type="table">
<listheader>
<term>Help Page</term>
<description>Function to call</description>
</listheader>
<item><term>List of types</term>
<description>GiveTypeListHTMLHelp</description></item>
<item><term>List of members</term>
<description>GiveMemberListHTMLHelp</description>
</item>
<item><term>Help for a single member</term>
<description>GiveMemberHTMLHelp</description></item>
</list>
</para>
</remarks>
<permission cref="!:">public</permission>
<example><code>
// Создать класс, осуществляющий преобразования
GiveHelpTransforms ght = new GiveHelpTransforms();
// Загрузить XML в свойство SourceXML
ght.LoadXMLFromFile(
"E:\\Inetpub\\wwwroot\\GiveHelp\\GiveHelpDoc.xml");
// Выполнить преобразование и вывести строку
Response.Write(
ght.GiveMemberHTMLHelp(Request.QueryString.Get("Type"),
Request.QueryString.Get("Member")) );
</code></example>
</member>
... 
<member name="P:GiveHelp.GiveHelpTransforms.SourceXML">
<value>
The SourceXML property contains the XML that will be used in
the transformations by the member functions for this class.
</value>
<permission cref="!:">public</permission>
</member>
</members>
</doc>

Если вы сравните его с исходными XML-комментариями (Листинг 1), то заметите, что некоторые из экспортированных комментариев были отформатированы. Тэги, приведенные в таблице 1, требуют при обработке особого форматирования. Почти все эти тэги форматируются раскрытием атрибута cref. То есть синтаксический анализатор C# изменяет исходное значение атрибута cref: во-первых, определяет классификацию значения (например, является ли оно типом или свойством), во-вторых, раскрывает значение в полное имя. Атрибут cref со значением «MyMember» (т. е. cref=«MyMember») после раскрытия синтаксическим анализатором может выглядеть так:

M:MyProject.MyType.MyMember

М: является классификатором. Компилятор, помещая в XML-документацию ссылки на элементы, классифицирует их одним из пяти способов. Допустимые классификаторы показаны в табл. 2.

Таблица 1. Результаты обработки тэгов

ТэгРезультат
<exception>Раскрывается атрибут cref
<permision>Раскрывается атрибут cref
<seealso>Раскрывается атрибут cref
<include>Тэги ссылки на включаемые файлы копируются в файл документации
<see>Раскрывается атрибут cref

Таблица 2. Классификаторы

ЗначениеПрименяется для:Префикс
ТипКлассов, делегатовT:
ПолеПеременных членовF:
МетодПроцедур и функцийM:
СвойствоСвойствP:
СобытиеСобытийE:
НеизвестноеСсылки, которую не удалось раскрыть!:

Другие части раскрытого имени из предыдущего примера тоже значимы. «MyNamespace.MyType.MyMember» — это полное имя (fully qualified name), определенное синтаксическим анализатором C#. Имя можно разбивать на части разделителем (точкой). Каждая часть имеет четкий смысл. MyNamespace представляет простанство имен. MyType - тип, а MyMember является именем элементоа (члена). Если раскрыть значение cref не удается, компилятор ставит перед исходным значением cref префикс ! (таблица 3):.

Примеры cref

Значение атрибута crefРаскрытое имя
cref="MyClass""T:MyProject:MyClass"
cref="MyField""F:MyProject:MyClass.MyField"
cref="MyMethod""M:MyProject:MyClass.MyMethod"
cref="MyProperty""E:MyProject:MyClass.MyProperty"
cref="MyEvent""P:MyProject:MyClass.MyEvent"
cref="MyUnknown""!:MyUnknown"

Такие классификаторы применяются не только при раскрытии атрибутов cref. Как вы вскоре убедитесь, в XML-документации этот формат используется и для имен элементов. Вот как выглядит базовая высокоуровневая структура XML-документа:


<?xml version="1.0"?>
<doc>
<assembly>
<name>Name</name>
</assembly>
<members>
<member name="name">
Сюда помещаются XML комментарии
</member>
... 
</members>
</doc>

Первая строка — стандартный заголовок, за которым идет корневой элемент <doc>. Следующий тэг — <assembly>. Под ним находятся тэги <name>, идентифицирующие имена сборок, созданных из кода проекта. Таким образом, обычно встречается только один элемент — <name>. Значение, подставляемое в него компилятором C#, соответствует имени проекта, и егонельзя изменить с помощью XML в исходном коде. Далее размещается блок <members>, содержащий записи для всех элементов, с которыми сопоставлены XML-комментарии. Каждый элемент идентифицируется собственным тэгом <member>. У тэга <member> единственный атрибут — name. Синтаксический анализатор C# генерирует значение этого атрибута в зависимости от имени элемента, с которым связанны тэги комментария. Остальной XML, находящийся внутри тэга <member>, состоит из XML-комментариев.

Генерация справочных файлов

Трудно перечислить все, что можно сделать с документом, в котором содержатся XML-комментарии, но чаще всего на его основе генерируются (с помощью XSLT) страницы справки в формате HTML. Более подробно об этом процессе описано в статье журнала MSDN Magazine за июль 2002 года.

Реклама

Ролик с скачать фильмы бесплатно хорошие Военный