C# XML Documentation

by Alan Dean

Subscribe to feeds:  Atom feed for Alan Dean Atom  | RSS feed for Alan Dean RSS  | RDF feed for Alan Dean RDF

 

Table of Contents

Preamble

One of the things that I really like about coding using C# in Visual Studio is the support provided for richly documenting my code.

I'm sure that the vast majority of C# developers are familiar with XML Documentation but I find myself often going to check up on the supported syntax for the supported tags or cribbing comments from old code to save time. So, as much for my own benefit as anything else, I decided to post an in-depth article on the capability because I think that the available documentation is not helpful enough. But for those who are not as familiar, here is the overview.

Overview

In order to leverage XML Documentation, the project must be configured to emit an xml file during the build process:

Visual Studio Build Page, Project Designer (C#)

One of the nice side-effects of setting this configuration value is that documentation warnings are then enabled:

XML Documentation warning

If you want to ensure that the documentation is kept up-to-date, then one option is to treat warnings as errors:

Treat warnings as errors

The warnings can be resolved by adding simple documentation:

Commenting a class

When this is built, the output xml looks like:

Output XML

Historically, code documentation only existed in-line to the code itself. However, XML Documentation allows the in-line code documentation to be leveraged to generate an external document set.

Before Visual Studio 2005, I used NDoc, an open-source utility that generated excellent documentation output. Unfortunately, the project has been effectively abandoned since the release of Framework 2.0. If you are interested, Scott Hanselman had some thoughts on NDoc: The Death of a (great) Open Source Project (it's about half-way down the page). For more information, see Wikipedia.

The natural successor to NDoc has been the Sandcastle offering from Microsoft. I don't plan on discussing too much about it right now, but essentially it is a documentation compiler. It doesn't have a GUI, but there are plenty of community GUIs available on CodePlex. Personally I like the Sandcastle Help File Builder (not least because it has a very similar look & feel to NDoc, which was very simple to use) but you may prefer one of the alternatives. You can also visit sandcastledocs.com for more information.

This is the Sandcastle Help File Builder GUI:

Sandcastle Help File Builder

After the build command is used, a Compiled HTML Help (.chm) file is emitted. Here is what it looks like:

Compiled HTML Help

Tags

This entry will look at the standard tags used by XML Documentation.

<see> source /  <seealso> source

This pair of tags are crucial to understand, as they are the means by which you can manage documentation links. The <see> tag is used inside other tags (and therefore inside a text block) whereas <seealso> exists as a 'top-level' tag on it's own which is rendered at the base of the documentation page. You cannot use <see> outside a tag, and you cannot use <seealso> inside a tag. This seems somewhat pedantic to me, as in all other respects the two tags are identical and it would have been easier just to have a single tag, but there you are.

Both tags must carry one of two attributes, either href="…" or cref="…"

The href="…" attribute specifies an external hyperlink. This can be to any resource that a browser will recognise, for example:

/// <summary>Visit the <see href="http://msdn.microsoft.com">MSDN Homepage</see>.</summary>
///
/// <seealso> href="http://msdn.microsoft.com">MSDN</seealso>

The target resource could equally be a Word document or a PDF file. I like to link to any relevant schema that the application may consume:

/// <seealso href="http://www.w3.org/2001/XMLSchema.xsd">W3C XML Schema 2001</seealso>

The cref="…" attribute specifies an internal hyperlink. An important point to be aware of is that when I say internal, I mean internal to the installed MSDN library on the local machine, not just to your application. This means that you can specify internal links to any framework target, which is very powerful.

Specifying the attribute unadorned, as documented by MSDN, results in the target being resolved as a Type:

/// <summary>Link to <see cref="System.Object"/>.</summary>

Interestingly, the cref attribute also takes some undocumented switches:

/// <summary>Link to a type (class, interface, struct or enum) <see cref="T:System.Exception"/>.</summary>

/// <summary>Link to a method <see cref="M:System.Exception.ToString"/>.</summary>

/// <summary>Link to a property <see cref="P:System.Exception.Message"/>.</summary>

/// <summary>Link to an event <see cref="E:System.AppDomain.AssemblyLoad"/>.</summary>

/// <summary>Link to a fixed value (const, enum item or static readonly field) <see cref="F:System.Int32.MaxValue"/>.</summary>

There is also another undocumented attribute of the <see> tag: the langword="…" attribute (this doesn't apply to <seealso>). Examples include:

<see langword="true"/>

<see langword="false"/>

<see langword="string"/>

<summary> source /  <remarks> source

These two tags are really two sides of the same coin. MSDN says:

The <summary> tag should be used to describe a type or a type member. Use <remarks> to add supplemental information to a type description.

The text for the <summary> tag is the only source of information about the type in IntelliSense, and is also displayed in the Object Browser and in the Code Comment Web Report.

/// <summary>Describe the type or member here.</summary>
///
/// <remarks>Add supplemental information here.</remarks>

<param> source

This tag is used to document a method parameter. It has a name attribute that must be set.

/// <summary>Describe the method here.</summary>
///
/// <param name="value">Describe the <see langword="int"/> value here.</param>
public void MyMethod(int value) { … }

<paramref> source

This tag is used to indicate that a word refers to a parameter.

/// <summary>Describe the method here.</summary>
///
/// <param name="value">Describe the <see langword="int"/> value here.</param>
///
/// <remarks>Make some remarks about the <paramref name="value"/> parameter.</remarks>
public void MyMethod(int value) { … }

<returns> source

This tag is used to describe the return value from a method.

/// <summary>Describe the method here.</summary>
///
/// <returns>Describe the return value of the method here.</returns>
public bool MyMethod() { … }

<value> source

This tag is used to describe a property value.

/// <summary>Describe the method here.</summary>
///
/// <value>Describe the property value here.</value>
public string MyProperty { … }

<exception> source

This tag is used to describe exceptions that may be thrown by a member.

/// <summary>Describe the method here.</summary>
///
/// <param name="obj">Describe the <see langword="object"/> parameter here.</param>
///
/// <exception cref="T:System.ArgumentNullException">
/// Thrown when the specified <paramref name="obj"/> is <see langword="null"/>.
/// </exception>
public void MyMethod(object obj) { … }

<event>

This tag is undocumented, but is used to describe events that may be raised by a member.

/// <summary>Describe the method here.</summary>
///
/// <event cref="T:Example.MyClass.MyEvent">
/// Describe the event here.
/// </event>
public void MyMethod() { … }

<permission> source

This tag is used to describe the defined permissions.

/// <summary>Describe the method here.</summary>
///
/// <permission cref="T:System.Security.Permissions.StrongNameIdentityPermission">
/// Demands that the caller be signed with the appropriate strong name key.
/// </permission>
public void MyMethod() { … }

<c> source /  <code> source

The <c> tag is used to describe in-line code:

/// <summary>Describe the method here.</summary>
///
/// <remarks>
/// The <see cref="M:Example.MyClass.MyMethod"/> method
/// does not require class initialization, use <c>MyClass.MyMethod()</c>. instead
/// </remarks>
public static void MyMethod() { … }

The <code> tag is used to describe multiline blocks of code:

/// <summary>Describe the method here.</summary>
///
/// <remarks>
/// The <see cref="M:Example.MyClass.MyMethod"/> method
/// requires class initialization, use:
/// <code>
/// MyClass obj = new MyClass();
/// obj.MyMethod();
/// </code>
/// </remarks>
public void MyMethod() { … }

<example> source

This tag describes the example section. It can contain formatted text and code:

/// <summary>Describe the class here.</summary>
///
/// <example>
/// The <see cref="T:Example.MyClass"/> class can be initialized directly:
/// <code>
/// MyClass obj = new MyClass();
/// </code>
/// or obtained from the <see cref="M:Example.MyClass.New()"/> factory method.
/// <code>
/// MyClass obj = MyClass.New();
/// </code>
/// </example>
public class MyClass() { … }

<para> source

This tag groups text into paragraphs:

/// <summary>Describe the class here.</summary>
///
/// <remarks>
/// <para>This is the first paragraph.</para>
/// <para>This is the second paragraph.</para>
/// </remarks>
public class MyClass() { … }

However, if you just want to force a line break then you can use <br/>. Note that you must use the XHTML notation as <br> will not be compiled. Similarly, you can insert an <hr/> if you want (but not <hr>).

<list> source

This tag describes formatted lists, and has three types available.

<list type="bullet">

The type="bullet" attribute renders to an unordered list. A bullet list can be simple:

/// <summary>Describe the method here.</summary>
///
/// <remarks>
/// A simple unordered list:
/// <list type="bullet">
/// <item>An item.</item>
/// <item>Another item.</item>
/// </list>
/// </remarks>
public void MyMethod() { … }

A bullet list can also be complex:

/// <summary>Describe the method here.</summary>
///
/// <remarks>
/// A complex unordered list:
/// <list type="bullet">
/// <item>
///   <term>An item</term>
///   <description>with a description.</description>
/// </item>
/// <item>
///   <term>Another item</term>
///   <description>with a description.</description>
/// </item>
/// </list>
/// </remarks>
public void MyMethod() { … }

Simple and complex bullet lists can also be blended:

/// <summary>Describe the method here.</summary>
///
/// <remarks>
/// A blended unordered list:
/// <list type="bullet">
/// <item>A simnple item</item>
/// <item>
///   <term>A complex item</term>
///   <description>with a description.</description>
/// </item>
/// </list>
/// </remarks>
public void MyMethod() { … }

<list type="number">

The type="number" attribute renders an ordered list:

/// <summary>Describe the method here.</summary>
///
/// <remarks>
/// A simple ordered list:
/// <list type="number">
/// <item>An item.</item>
/// <item>Another item.</item>
/// </list>
/// </remarks>
public void MyMethod() { … }

A number list can also be complex:

/// <summary>Describe the method here.</summary>
///
/// <remarks>
/// A complex ordered list:
/// <list type="number">
/// <item>
///   <term>An item</term>
///   <description>with a description.</description>
/// </item>
/// <item>
///   <term>Another item</term>
///   <description>with a description.</description>
/// </item>
/// </list>
/// </remarks>
public void MyMethod() { … }

Simple and complex number lists can also be blended:

/// <summary>Describe the method here.</summary>
///
/// <remarks>
/// A blended ordered list:
/// <list type="number">
/// <item>A simnple item</item>
/// <item>
///   <term>A complex item</term>
///   <description>with a description.</description>
/// </item>
/// </list>
/// </remarks>
public void MyMethod() { … }

<list type="table">

The type="table" attribute renders a two-column table:

/// <summary>Describe the method here.</summary>
///
/// <remarks>
/// A table list:
/// <list type="table">
/// <listheader>
///   <term>Column 1</term>
///   <description>Column 2</description>
/// </listheader>
/// <item>
///   <term>item</term>
///   <description>description</description>
/// </item>
/// </list>
/// </remarks>
public void MyMethod() { … }

<include> source

This tag refers to an external file containing documentation. However, this is one tag that I am going to step over. I found that using it was ungainly, awkward and difficult to maintain.

<???>

One of the nice aspects of this approach to documentation is that you are at liberty to extend the set of tags whenever you want. For example, you may choose to document the author:

/// <summary>Describe the method here.</summary>
///
/// <author email="alan.dean@thoughtpad.net">Alan Dean</author>
public void MyMethod() { … }

or a defect reference:

/// <summary>Describe the method here.</summary>
///
/// <defect id="1234" href="http://example.com/defect/1234.html">title / description</defect>
public void MyMethod() { … }

Obviously, this tag will not be supported by any tool because you have just invented it. This will mean that the tag will not be rendered. However, the data will be present in the output xml file from where you can leverage it.

<overloads>

An example of such an extension is the <overloads> tag invented by NDoc, and now supported by Sandcastle. This extension permits the description of a common summary when a constructor or method is overloaded:

/// <overloads>Describe the method summary here.</overloads>
///
/// <summary>Describe the method taking an integer parameter here.</summary>
///
/// <param name="value">Describe the <see langword="int"/> value here.</param>
public void MyMethod(int value) { … }

/// <summary>Describe the method taking a string parameter here.</summary>
///
/// <param name="value">Describe the <see langword="string"/> value here.</param>
public void MyMethod(string value) { … }

Advanced Features

One of the restrictions that I have found with the regular xml documentation tags is the restricted table rendering capability via the <list type="table">…</list> tag.

Thankfully, you can render more complex tables, as below:

/// <summary>Describe the method taking a string parameter here.</summary>
///
/// <remarks>
/// <div class="tablediv">
/// <table>
/// <tr valign="top">
/// <th>Column 1</th>
/// <th>Column 2</th>
/// <th>Column 3</th>
/// </tr>
/// <tr valign="top">
/// <td>a</td>
/// <td>b</td>
/// <td><see cref="T:Example.MyClass">Class description</see></td>
/// </tr>
/// </table>
/// </div>
/// </remarks>
public void MyMethod() { … }