Automate the Process of Documenting Your Code

Introduction

Every developer relies on a development platform; the two major ones being .NET and J2EE. The development platform provides the basic plumbing, such as file IO, threading, XML processing, and much more so we developers don't have to worry about the low-level plumbing itself. You use the classes and methods provided by the development platform. Both platforms have a rather large class library and you rely on a comprehensive documentation to understand it and to be able to use it. The same holds true for your own projects and products. Every product needs to constantly innovate and be expanded to survive in the market place. This means your own class library is growing and you get quickly to the point where your team also relies on good documentation to be able to understand, use, and improve it. This holds very true when you bring in new team members.

We all understand the need to comprehensively document our class libraries and to keep them up to date with the code base. And still, documentation is an ongoing pain for all of us. We want better documentation about the development platform we use and your team requires better documentation about each others code. Many people experienced the pain trying to keep external documentation up to date with an ever changing code base and also finding the right documentation when looking at some piece of code. The Visual Studio .NET IDE takes a first big step in easing that pain. It allows you to document your code in a way that the compiler can create an XML document describing your classes, methods, properties, and so forth. This article explains how to use this feature and then create a basic help file out of it.

How to Create XML Documentation Tags in VS.NET

How this works is that you document your code using XML tags and, when you compile your project, the compiler looks for those tags and uses them to create a XML file describing your classes, methods, properties, and so on. This has been available in C# since Visual Studio .NET 2003 and in VB.NET since Visual Studio .NET 2005. In an empty line above your class, method, or property declaration, type three slashes in C#, or three apostrophes in VB.NET. The IDE now creates a XML structure in front of your class, method, or property that exactly reflects its signature (see example below). You then type in the documentation of the class, method, or property.

/// <summary>
/// the form that allows you to choose the XML and XSLT document
/// and then perform the transformation to create the help page
/// </summary>
partial class CodeDocumenter : Form
{
   /// <summary>
   /// user can choose the XML source file to use
   /// </summary>
   /// <param name="sender">event sender</param>
   /// <param name="e">event arguments</param>
   private void SourceFilePicker_Click(object sender, EventArgs e)
   {
   }
}

Methods have also an XML property tag for each argument. This allows you to describe each argument in detail. It also creates an XML tag for the return value itself so you can describe the expected return value. Any change to the method signature also requires keeping the XML tags in sync. It is also important to keep the order of the XML tags describing each argument in sync with the order of your arguments in the method signature. So, if you add a new argument at the beginning of your method signature, you also need to add the XML tag describing this argument before all the other ones. First, add a new line at the appropriate position, which already adds the three slashes or apostrophes for you. Then, type the left angle bracket (<), which opens up an Intellisense menu and shows you the "param name = 'argument name'". Select it and add the right angle bracket (>); the IDE creates the complete XML tag for you and you add the description.

The Intellisense menu also shows additional XML tags that are available. You can, for example, add a permission tag describing the permissions callers need to have to call and execute the method successfully. You can use the example tag to show an example how to use the method, and so forth. This makes it very easy to document your classes, methods, and properties comprehensively.

How to Create the XML Documentation File

You need to tell the compiler to create an XML documentation file using all the XML documentation tags you added in your code. You can do this through your project properties. The steps are different for VB.NET and C#. In C#, select the "Build" tab and, under the "Output" group, select the "XML Documentation File" option. Beside the option is a textbox with the path and name of the XML file. This is set by default to the same path where your binary is created plus the project name as the file name, for example "bin\Debug\CodeDocumenter.XML". In VB.NET, select the "Compile tab" and, at the bottom, check the "Generate XML documentation file" option. This creates the file at the same location where your binary is created, which is by default the BIN folder under your project folder, an XML documentation file (the same name as your project file). Next, compile your code and look for the XML documentation file. Here is a sample XML documentation file for the code snippet shown above.

<?xml version="1.0" ?>
<doc>
   <assembly>
      <name>CodeDocumenter</name>
   </assembly>
   <members>
      <member name="T:CodeDocumenter.CodeDocumenter">
         <summary>
         the form that allows you to choose the XML and XSLT
         documents and then perform the transformation to create
         the help page
         </summary>
      </member>
      <member name="M:CodeDocumenter.CodeDocumenter.
                   SourceFilePicker_Click
                   (System.Object,System.EventArgs)">
         <summary>
         user can choose the XML source file to use
         </summary>
         <param name="sender">event sender</param>
         <param name="e">event arguments</param>
      </member>
   </members>
</doc>

Any missing XML tag or mismatch between the XML tags and class, method, or property signature will be show as a warning. It is important to resolve this to assure that documentation and code stay in sync. As soon as your team gets negligent on that, you will see the documentation becomes less and less valuable until you reach the point that it is out-of-sync and no longer used. This is a discipline you need to instill in your development team.

How the Visual Studio .NET IDE Uses the XML Documentation File

The Visual Studio .NET IDE also takes advantage of the XML documentation file. The Intellisense feature of the IDE will look for the XML documentation file at the same location as the referenced binary file. If present, it will use the XML documentation file when showing the Intellisense and show right in your IDE your descriptions of the classes, methods, method arguments, return values, properties, and so on. This can be extremely useful for other teams and team members. You get helpful information shown right when you have a need to use the class, method, or property. So, when distributing your binaries to other teams or team members, make sure to pass along the XML documentation file. They will appreciate that little bit of convenience.

How to Create a Help File for Your Code

You can use the same XML documentation file to create help files describing your code. This is useful if you are selling components within the developer community. But, it is also useful to create help files describing your code each time you create a release. It can be used by your support teams, integration partners, or by your sustained maintenance team (the team that does the bug fixing after a release). The biggest advantage of this approach is that code and documentation are kept in sync. Even if you have separate technical writers creating your code documentation, they can still use the help files generated out of the XML documentation files as a starting point. This cuts down the time they need to gather information.

The simplest way to create a help file is to apply an XSL transformation to the XML documentation file that creates a HTML file. The enclosed tool allows you to select an XML documentation file, an XSL stylesheet, perform the transformation, and save it as an HTML help file. Here is how the XSL stylesheet provided by the tool works. First, the template for the matching root node gets called. This one selects all assembly nodes and calls its associated template.

<!-- root node: calls the assembly match which then calls the
     members match -->
<xsl:template match="/">
  <xsl:apply-templates select="//assembly"/>
</xsl:template>

The assembly template includes the CSS stylesheet provided by the tool. The tool creates a copy of that CSS stylesheet at the same location it saves the generated HTML help file. All HTML code generated references styles defined in the CSS stylesheet, so it becomes easy to change the formatting of the generated help file later on. Next, it creates an HTML table to describe the assembly. It then displays the assembly name in the first row of this table. Nextn it selects all member nodes that have a name attribute containing the "T:" characters. The XML documentation file adds all types as member nodes with a name attribute always starting with "T:". So, effectively, we select all types and call its appropriate template.

<!-- assembly: build the help table with the title of the
     assembly -->
<xsl:template match="assembly">
   <LINK REL="STYLESHEET" TYPE="TEXT/CSS"
         HREF="HelpStyleSheet.css"/>
   <TABLE CLASS="AsemblyTable" CELLPADDING="2" CELLSPACING="0">
      <TR CLASS="AssemblyHeader">
         <TD COLSPAN="3">
            <xsl:text>Assembly </xsl:text>
            <FONT CLASS="Title"><xsl:value-of select="name"/></FONT>
         </TD>
      </TR>
      <xsl:apply-templates select="//member[contains(@name,'T:')]"/>
   </TABLE>
</xsl:template>

Automate the Process of Documenting Your Code

The member template first creates three variables. The first variable stores the name of the member. This is done by cutting off the "T:" prefix and also removing the assembly name. Looking at the XML documentation file, you see that each type is added by using the syntax "T:assembly-name:type-name". The second member variable stores the type prefix and the assembly name. The last variable stores a filter that we use when selecting all members of this type, which is effectively "assembly-name.member-name.". Then, we add for each processed type a row to the assembly table created in the assembly template. We add an empty column at the beginning and at the end for positioning purposes. The middle column contains the information about the type itself. It creates another HTML table to display all the type information. In the first row, it shows the type name and, if present, its description. Then, it calls the ProcessTypeMembers template and passes along a collection of nodes, which are all members for the processed type as well as the name of the processed type.

<!-- process all types listed in the XML file -->
<xsl:template match="//member[contains(@name,'T:')]">

   <!-- value of processed type; without assembly name &
        member identifier -->
   <xsl:variable name="MemberValue" 
                 select="substring(@name, $AssemblyNameLength + 4,
                 string-length(@name) - $AssemblyNameLength - 3)"/>

   <!-- get member identifier and assembly name that is before
        the member name -->
   <xsl:variable name="IdentifierAndAssemblyName"
                 select="substring-before(@name, $MemberValue)"/>

   <!-- get member filter; it queries all members of the
        processed type -->
   <xsl:variable name="MemberFilter"
                 select="concat(concat(substring(
                 $IdentifierAndAssemblyName, 3, string-length(
                 $IdentifierAndAssemblyName) - 2),
                 $MemberValue), '.')"/>

   <TR CLASS="AssemblyBody">
      <TD>
         <xsl:text>  </xsl:text>
      </TD>
      <TD>
         <TABLE CLASS="TypeTable" CELLPADDING="2" CELLSPACING="0">
            <TR CLASS="TypeHeader">
               <TD COLSPAN="2">
                  <xsl:text>Type </xsl:text>
                  <FONT CLASS="Title"><xsl:value-of
                        select="$MemberValue"/></FONT>

                  <!-- output the type description if present -->
                  <xsl:if test="string-length(summary) > 0">
                     <xsl:text> - </xsl:text>
                     <xsl:value-of select="summary"/>
                  </xsl:if>
               </TD>
            </TR>

            <!-- call template to process type members & pass along
                 all members -->
            <xsl:call-template name="ProcessTypeMembers">
               <xsl:with-param name="Members"
                               select="//member[contains(@name,
                               $MemberFilter)]"/>
               <xsl:with-param name="TypeName"
                                  select="concat($MemberValue,
                                                 '.')"/>
            </xsl:call-template>
         </TABLE>
      </TD>
      <TD>
        <xsl:text>  </xsl:text>
      </TD>
   </TR>
</xsl:template>

The ProcessTypeMembers template does the work of adding to the type table all members now (please download the code attached to this article). It loops through the member nodes that were passed along. For each member node, it first gets the member name by cutting off the member identifier (can be "M:" for method, "F:" for field, "P:" for property, and so forth), the assembly, and type name. The format used in the XML documentation file is "member-identifier:assembly-name.type-name.member-name". It then performs the following logic:

  • Constructors—If it finds the string "#ctor" in the name attribute of the member node, it knows it is a constructor. It adds a new row to the type table, the first column showing that this is a constructor and the second column showing the description in the summary tag.
  • Property—If it finds the member identifier "P:" in the name attribute, it knows that the member is a property. It adds a new row to the type table, the first column showing that this is a property and the second column showing the name of the property with its description in the summary tag.
  • Field—If it finds the member identifier "F:" in the name attribute, it knows that the member is a field. It adds a new row to the type table, the first column showing that this is a field and the second column showing the name of the field with its description in the summary tag.
  • Type—If it finds the identifier "T:", it knows that the node represents the type itself and ignores it. The type node itself is also selected and passed along to the ProcessTypeMembers template, but no additional processing is needed.
  • Method—If it finds the member identifier "M:" in the name attribute, it knows that the member is a method. It adds a new row to the type table, the first column showing that this is a method and the second column showing the name of the method with its description in the summary tag. It then adds another table for all method arguments. It selects all param nodes for the processed member node and outputs the argument name and its description. It then also checks whether there is a return node, in case the method has a return value. In that case, it adds another row to the argument table with the name return value and the description of the return value.
  • Unknown—All other nodes are considered as unknown. It adds a new row to the type table, the first column showing that this is an unknown member and the second column showing the name of the member with its description in the summary tag.

Again, please refer to the attached code for the complete XSL stylesheet. If you want your help file to be structured, use the attached XSL stylesheet as a template and modify it as needed. You also can create multiple XSL stylesheets and select the one to use when performing the transformation. The attachment also includes the CSS stylesheet referenced by the generated help file (called HelpStyleSheet.css). Modify it to change the font-family, font-size, font-color, and more.

Summary

The value of the documentation capabilities built into the Visual Studio .NET IDE are often overlooked. They are easy to use and can improve the productivity of your development team quite noticeably. As this article shows, there are ways to further maximize the value of these documentation capabilities. It can reduce the costs associated with documenting your code quite a bit. This holds very true when you need to provide such documentation to members outside of the team, whether these are internal teams involved in supporting the product post release or external customers. There are professional tools available on the market and there are freeware tools available that are built by using the XML documentation file.

The following article shows how you can utilize the VS.NET 2003 Power Toys to generate a help file out of the XML documentation file and incorporate that help file into the VS.NET IDE so it is available right within your IDE. The attached example shows how easy it is to build such an infrastructure. If you have comments on this article or this topic, please contact me @ klaus_salchner@hotmail.com. I want to hear if you learned something new. Contact me if you have questions about this topic or article.

About the Author

Klaus Salchner has worked for 14 years in the industry, nine years in Europe and another five years in North America. As a Senior Enterprise Architect with solid experience in enterprise software development, Klaus spends considerable time on performance, scalability, availability, maintainability, globalization/localization, and security. The projects he has been involved in are used by more than a million users in 50 countries on three continents.

Klaus calls Vancouver, British Columbia his home at the moment. His next big goal is running the New York marathon in 2005. Klaus is interested in guest speaking opportunities or as an author for .NET magazines or Web sites. He can be contacted at klaus_salchner@hotmail.com or http://www.enterprise-minds.com.

Enterprise application architecture and design consulting services are available. If you want to hear more about it, contact me! Involve me in your projects and I will make a difference for you. Contact me if you have an idea for an article or research project. Also, contact me if you want to co-author an article or join future research projects!



Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Wednesday, September 24, 2014 8:00 AM - 9:00 AM PDT According to a recent Forrester Research report, many companies are choosing low-code platforms over traditional programming platforms, due to the speed with which low-code apps can be assembled and tested. With customer-facing applications on the rise, traditional programming platforms simply can't keep up with the "short schedules and rapid change cycles" required to develop these applications. Check out this upcoming webinar and join Clay Richardson from …

  • This ESG study by Mark Peters evaluated a common industry-standard disk VTl deduplication system (with 15:1 reduction ratio) versus a tape library with LTO-5, drives with full nightly backups, over a five-year period.  The scenarios included replicated systems and offsite tape vaults.  In all circumstances, the TCO for VTL with deduplication ranged from about 2 to 4 times more expensive than the LTO-5 tape library TCO. The paper shares recent ESG research and lots more. 

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds