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 similar to C# single-line comments, but start
with ///
(that’s three slashes),
and can be applied to any user-defined type or member. As well as containing
descriptive text, these comments can also include 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.
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>This tag describes the return values for a method.
-
<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>This tag describes a property on a class.
-
<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 |
---|---|
N
|
Namespace |
T
|
Type (class, struct, enum, interface, delegate) |
F
|
Field |
P
|
Property (includes indexers) |
M
|
Method (includes special methods) |
E
|
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[] p1, 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 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.