C# offers three different styles of source-code documentation: single-line comments, multiline comments, and documentation comments.
Single- and multiline
comments use
the C++ syntax, //
and /*...*/
:
int x = 3; // this is a comment MyMethod( ); /* this is a comment that spans two lines */
The disadvantage of this style of commenting is that there is no predetermined standard for documenting types. Consequently, it cannot be easily parsed to automate the production of documentation. C# improves on this by allowing you to embed documentation comments in the source, and by providing an automated mechanism for extracting and validating documentation at compile time.
Documentation comments
are composed of embedded XML tags.
These tags allow one to mark up the descriptive text to better define
the semantics of the type or member, and also to incorporate
cross-references. Documentation comments must either start with
///
(that’s
three slashes), or (as of Visual C# 2003) be
contained in multiline comments (that’s /* ... */
). They can be applied to any user-defined type or
member.
These comments can then be extracted at compile time into a separate output file containing the documentation. The compiler validates the comments for internal consistency, expands cross-references into fully qualified type IDs, and outputs a well-formed XML file. Further processing is left up to you, although a common next step is to run the XML through XSLT, generating HTML documentation.
Here is an example of documentation comments for a very simple type:
// Filename: DocTest.cs using System; class MyClass { /// <summary> /// The Foo method is called from /// <see cref="Main">Main</see> /// </summary> /// <mytag>Secret stuff</mytag> /// <param name="s">Description for s</param> static void Foo(string s) { Console.WriteLine(s); } static void Main( ) { Foo("42"); } }
When run through the compiler
using the
/doc:<
filename>
command-line option, the following XML file is generated:
<?xml version="1.0"?> <doc> <assembly> <name>DocTest</name> </assembly> <members> <member name="M:MyClass.Foo(System.String)"> <summary> The Foo method is called from <see cref="M:MyClass.Main">Main</see> </summary> <mytag>Secret stuff</mytag> <param name="s">Description for s</param> </member> </members> </doc>
The <?xml...>
,
<doc>
, and
<members>
tags are generated automatically
and form the skeleton for the XML file. The
<assembly>
and
<name>
tags indicate the assembly this type
lives in. Every member that was preceded by a documentation comment
is included in the XML file via a <member>
tag with a name
attribute that identifies the
member. Note that the cref
attribute in the
<see>
tag has also been expanded to refer to
a fully qualified type and member. The predefined XML documentation
tags that were embedded in the documentation comments are also
included in the XML file, and have been validated to ensure that all
parameters are documented, that the names are accurate, and that any
cross-references to other types or members can be resolved. Finally,
any additional user-defined tags are transferred verbatim.
The predefined set of XML tags that can be used to mark up the descriptive text are listed here:
-
<summary>
<summary>
description
</summary>This tag describes a type or member. Typically,
<summary>
contains the description of a member at a fairly high level.-
<remarks>
<remarks>
description
</remarks>This tag provides additional information regarding a particular member. Information about side effects within the method, or particular behavior that may not otherwise be intuitive (such as the idea that this method may throw an
ArrayOutOfBoundsException
if a parameter is greater than 5) is listed here.-
<param>
<param name="
name
">description</param>This tag describes a parameter on a method. The
name
attribute is mandatory, and must refer to a parameter on the method. If this tag is applied to any parameter on a method, all of the parameters on that method must be documented. Enclosename
in double quotation marks (“”).-
<returns>
<returns>
description
</returns>-
<exception>
<exception [cref="
type
"]>description
</exception>This tag documents the exceptions a method may throw. If present, the optional
cref
attribute should refer to the type of the exception. The type name must be enclosed in double quotation marks (“”).-
<permission>
<permission [cref="
type
"]>description
</permission>This tag documents the permissions requirement for a type or member. If present, the optional
cref
attribute should refer to the type that represents the permission set required by the member, although the compiler does not validate this. The type name must be enclosed in double quotation marks (“”).-
<example>
<example>
description
</example>This tag provides a description and sample source code explaining the use of a type or member. Typically, the
<example>
tag provides the description and contains the<c>
and<code>
tags, although they can also be used independently.-
<c>
<c>
code
</c>This tag indicates an inline code snippet. Typically, this tag is used inside an
<example>
block (described previously).-
<code>
<code>
code
</code>This tag is used to indicate multiline code snippets. Again, this is typically used inside of an
<example>
block (described previously).-
<see>
<see cref="
member
">text
</see>This tag identifies cross-references in the documentation to other types or members. Typically, the
<see>
tag is used inline within a description (as opposed to the<seealso>
tag, which is broken out into a separate “See Also” section). This tag is useful because it allows tools to generate cross-references, indexes, and hyperlinked views of the documentation. Member names must be enclosed by double quotation marks (“”).-
<seealso>
<seealso cref="
member
">text
</seealso>This tag identifies cross-references in the documentation to other types or members. Typically,
<seealso>
tags are broken out into a separate “See Also” section. This tag is useful because it allows tools to generate cross-references, indexes, and hyperlinked views of the documentation. Member names must be enclosed by double quotation marks (“”).-
<value>
<value>
description
</value>-
<paramref>
<paramref name="
name
"/>This tag identifies the use of a parameter name within descriptive text, such as
<remarks>
. The name must be enclosed by double quotation marks (“”).-
<list>
<list type=[ bullet | number | table ]> <listheader> <term>
name
</term> <description>description
</description> </listheader> <item> <term>name
</term> <description>description
</description> </item> </list>This tag provides hints to documentation generators about how to format the documentation—in this case, as a list of items.
-
<para>
<para>
text
</para>This tag sets off the text as a paragraph to documentation generators.
-
<include>
<include file='
filename
' path='path-to-element
'>This tag specifies an external file that contains documentation and an XPath path to a specific element in that file. For example, a path of
docs[@id="001"]/*
retrieves whatever is inside of<docs id="001"/>
. Thefilename
andpath
must be enclosed by single quotation marks (''), but you must use double quotation marks (“”) for theid
attribute within thepath-to-element
expression.
There is little that is special
about
the predefined XML tags recognized by the C# compiler, and you are
free to define your own. The only special processing done by the
compiler is on the <param>
tag (in which it
verifies the parameter name and that all the parameters on the method
are documented) and the cref
attribute (in which
it verifies that the attribute refers to a real type or member, and
expands it to a fully qualified type or member ID). The
cref
attribute can also be used in your own tags,
and is verified and expanded just as it is in the predefined
<exception>
,
<permission>
,
<see>
, and
<seealso>
tags.
Once an XML file is generated from documentation comments, it typically requires another step before becoming generally useful to programmers, since most of us aren’t quite able to parse and use an XML file “in the raw.”
The most common approach is to run the XML output through an XSLT stylesheet to generate some more human-friendly format, such as HTML. (Variations on this idea include generating PDF, RTF, or even Microsoft Word documents.) While Microsoft may publish a standardized XSLT file for this, companies may want to create their own to establish their own “look” for documentation of components they sell. Alternatively, several open source projects have already begun to explore this area, and produce neatly formatted documentation files; one example is http://ndoc.sourceforge.net.
The ability to put user-defined tags into the XML documentation
sections represents a powerful extensibility point—for example,
a company may put implementation details about the method or class
inside of <implementation>
tags and define
two XSLT files: one excluding the implementation tags, and the other
including it. The non-<implementation>
-aware
files can be distributed publicly, while the
<implementation>
-aware files are used
internally by developers. An automated test tool might run tests
against those methods or classes described by a <test name=test-to-run>
tag. Types meant to be stored in the
database might use XML documentation tags to indicate the SQL
required to create the tables, and a custom tool could be written to
extract the necessary SQL from the XML documentation files in order
to run it. Non-public information about the code can be embedded
within the code itself or in the documentation comments and processed
by an external tool via this mechanism, which makes it quite
powerful.
Type names and type or member cross-references are translated into IDs that uniquely define the type or member. These names are composed of a prefix that defines what the ID represents and a signature of the type or member.
Table 4-4 lists the set of type or member prefixes.
Table 4-4. XML type ID prefixes
XML type prefix |
ID prefixes applied to |
---|---|
|
Namespace |
|
Type (class, struct, enum, interface, delegate) |
|
Field |
|
Property (includes indexers) |
|
Method (includes special methods) |
|
Event |
|
Error |
The rules describing how the signatures are generated are well documented, although fairly complex.
Here is an example of a type and the IDs that are generated:
// Namespaces do not have independent signatures namespace NS { /// T:NS.MyClass class MyClass { /// F:NS.MyClass.aField string aField; /// P:NS.MyClass.aProperty short aProperty {get {...} set {...}} /// T:NS.MyClass.NestedType class NestedType {...}; /// M:NS.MyClass.X( ) void X( ) {...} /// M:NS.MyClass.Y(System.Int32,System.Double@,System.Decimal@) void Y(int p1, ref double p2, out decimal p3) {...} /// M:NS.MyClass.Z(System.Char[ ],System.Single[0:,0:]) void Z(char[ ] 1, float[,] p2) {...} /// M:NS.MyClass.op_Addition(NS.MyClass,NS.MyClass) public static MyClass operator+(MyClass c1, MyClass c2) {...} /// M:NS.MyClass.op_Implicit(NS.MyClass)~System.Int32 public static implicit operator int(MyClass c) {...} /// M:NS.MyClass.#ctor MyClass( ) {...} /// M:NS.MyClass.Finalize ~MyClass( ) {...} /// M:NS.MyClass.#cctor static MyClass( ) {...} } }
Get C# in a Nutshell, Second Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.