`
love19820823
  • 浏览: 954436 次
文章分类
社区版块
存档分类
最新评论

把 Visual Studio .NET 源代码文件中C# XML注释提取成工程文档

 
阅读更多

这篇文章假设你对 XML, XSLT, 和 C# 熟悉
下载这篇文章的源代码: XMLC.exe (76KB)
译者说明:这篇文章是很早以前就发表了,它提供的源代码是基于 VS.net 测试版(RTM 和 Beta 2)的。
摘要

C# 允许开发人员在源代码中插入XML注释,这在多人协作开发的时候显得特别有用。 C#解析器可以把代码文件中的这些XML标记提取出来,并作进一步的处理为外部文档。这篇文章将展示如何使用这些XML注释。作者演示了如何生成工程,如何把XML注释输出为有用文档,如何把这些注释转变为帮助文件。在项目开发中,很多人并不乐意写繁杂的文档。但是,开发组长希望代码注释尽可能详细;项目规划人员希望代码设计文档尽可能详尽;测试、检查人员希望功能说明书尽可能详细等等。 如果这些文档都被要求写的话,保持它们同步比进行一个战役还痛苦。
为何不把这些信息保存在一个地方呢??最明显想到的地方就是代码的注释中;但是你很难通览程序,并且有些需要这些文档的人并不懂编码。
这篇文章将展示如何通过使用XML注释来解决这些问题。代码注释、用户手册、开发人员手册、测试计划等很多文档可以很方便的从XML注释中获得。我将先演示如何插入XML注释、如何把这些XML注释导出为另一个文档。然后再讨论每个XML标记的意思,以及使用XML和XSL生成帮助文件。

XML 注释
所有的XML注释都在三个向前的斜线之后(///)。两条斜线表示是一个注释,编译器将忽略后面的内容。三条斜线告诉编译器,后面是XML注释,需要适当地处理。
当开发人员输入三个向前的斜线后,Microsoft Visual Studio .NET IDE 自动检查它是否在类或者类成员的定义的前面。如果是的话,Visual Studio .NET IDE 将自动插入注释标记,开发人员只需要增加些额外的标记和值。下面就是在成员函数前增加三个斜线,自动增加的注释:
/// <summary>
///
/// </summary>
/// <param name="strFilePath"></param>
public void LoadXMLFromFile(string strFilePath)

这里嵌入的标记仅仅是Visual Studio .NET IDE 的一部分标记,然而在IntelliSense for xml中,并没有把c#规范中所有的标记列出来,遗失的部分只能用手工插入。
这些手工标记是非常有用的,如果恰当地设置他们,对导出成外部说明文件将非常有帮助。
如果碰到是预先定义的xml标记,编译器可以把这些注释以文本输出;如果是编译器不认识的xml标记,编译器会逐字的把所有内容都输出,这意味着你可以使用自己定义的标记。
适当的设置,C#编译器并不格式化xml注释,而是以xml文件输出。下面的步骤将演示如何让C#编译器把xml注释输出为xml文件
  • 右键点击解决方案资源管理器中的工程项,打开工程的属性页,点击属性。
  • 对话框出现后,点击配置属性目录。
  • 点击生成节点。
  • 在右边的框中有 输出--xml文档文件,这里就是设置丛xml注释生成xml文件的文件名。(注:这里设置的是相对路径,不是绝对路径)
在我的例子中,我使用GiveHelpDoc.xml作为文档名(看图一)。如果这里没有输入任何值,这将是默认设置,xml注释将不会以外部文件形式输出。
图一 配置xml注释输出

图一:配置xml注释输出

公认的标记
我把xml注释标记分类两大类:第一类是设置标记,我叫他们主标记。他们在一组标记的开始,而且不会在标记内部出现。第二类是设置标记包含的标记,我叫他们支持标记。支持标记和html标记的区别在于:支持标记采用的是小写,html标记采用的是大写。图2显示了我将要讨论的支持标记和主标记。
图2 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>
    /// // create the class that does translations
    /// GiveHelpTransforms ght = new GiveHelpTransforms();
    /// // have it load our XML into the SourceXML property
    /// ght.LoadXMLFromFile("E:\\Inetpub\\www-
    /// root\\GiveHelp\\GiveHelpDoc.xml");
    ///
    /// // do the translation and then write out the string
    /// 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"/> 
        /// and translate it to a list of Members in the specified type.
        /// </summary>
        /// <param name="strType">The fully qualified name of the type that 
        /// the member is in.</param>
        /// <returns>The HTML that lists the types that are in the XML 
        /// documentation.</returns>
        /// <seealso cref="GiveTypeListHTMLHelp"/>
        /// <seealso cref="GiveMemberHTMLHelp"/>
        /// <example><code>
        /// // create the class that does translations
        /// GiveHelpTransforms ght = new GiveHelpTransforms();
        /// // have it load our XML into the SourceXML property
        /// ght.LoadXMLFromFile(
        ///            "E:\\Inetpub\\wwwroot\\GiveHelp\\GiveHelpDoc.xml");
        ///
        /// // do the translation and then write out the string
        /// Response.Write( 
        /// ght.GiveMemberListHTMLHelp(Request.
        /// QueryString.Get("Type")));
        /// </code></example>
        /// <permission cref="">public</permission>
        public string GiveMemberListHTMLHelp(string strType)
        {
            // Load the corresponding XSLT
            XslTransform xslTransformFile = new XslTransform();
            xslTransformFile.Load(
                "E:\\Inetpub\\wwwroot\\GiveHelp\\GiveTypeMemberListHelp.xsl");

            // create the output repository for the transform
            System.Text.StringBuilder sbTransformed = 
                    new System.Text.StringBuilder();
            System.IO.TextWriter tw = 
                (System.IO.TextWriter)new 
                 System.IO.StringWriter(sbTransformed);

            // the strType parameter is passed into the stylesheet
            XsltArgumentList arglist = new XsltArgumentList();
            arglist.AddParam("WhichType","",strType);

            xslTransformFile.Transform(m_xdSourceXML,arglist,tw);
    
            return tw.ToString();
        }
   }     
        
这里有九个主标记: <remarks>, <summary>, <example>, <exception>, <param>, <permission>, <returns>, <seealso>, 和<include>.
在上下文中,<remarks>描述了这个类的细节。
/// <remarks>
/// Class that contains functions to do
/// transformations to help files.
/// </remarks>

C#文档推荐使用<remarks>描述一个对象,<summary>描述一个对象成员。奇怪的是如果你用 Visual Studio .NET IDE在一个类前输入///,他将自动增加<summary>,而不是<remarks> ,这时你需要手工输入<remarks>。
<summary>经常在C#文档中发现,这个标记用于描述类的成员,包括属性、方法、成员等。
/// <summary>
/// This XmlDocument based attribute contains the
/// XML documentation for the project.
/// </summary>

<summary>也可以很好的描述对象,但是不推荐,xml注释文档推荐使用<remarks>描述对象。
<example>被用来演示如何使用,例子可以使任何正确的文本文件,但是更多是一小段代码:
/// <example>
/// <code>
/// // create the class that does translations
/// GiveHelpTransforms ght = new GiveHelpTransforms();
/// // have it load our XML into the SourceXML property
/// ght.LoadXMLFromFile(
///  "E:\\Inetpub\\wwwroot\\GiveHelp\\GiveHelpDoc.xml");
/// </code>
/// </example>

如果其中有代码,<code>经常被使用,<code>是将要被支持的一个标记。
<exception> 处理异常文档。如果多于一个异常会被抛出,你就需要多个<exception>。 <exception>有一个属性:cref 这个属性值是将被扔出异常的类名。这个类名必须是正确的,因为C#编译器将检查这一项,后面我将具体讨论。
这篇文章的代码将不会扔出任何异常,但是下面有个代码例子,演示如何使用<exception>:
/// <exception cref="SampleException">
/// Normally, a discussion on any exceptions thrown would go here.
/// </exception>

<param>用于描述方法、属性的参数。当你在一个方法前面输入/// 他会被自动的插入。<param>有个属性,name 用于记录源代码中参数的名字。
/// <param name="strFilePath">The path to the file containing the
/// source XML.</param>

类成员访问修饰符是通过 <permission>来记录,它的值用来写明访问限制,值不是必需的。值可以是下面几种:public, private, protected等。Scope是另外一个标记,用于表示是不是static的方法。
<permission>有个属性cref。cref 属性的值用来表示调用当前编译环境的一个类成员或者属性,它通常在System.Security.PermissionSet中定义。
/// <permission cref="System.Security.PermissionSet">Public
/// Access</permission>
<returns>跟 <param> 相类似,他用来描述方法、属性的返回值。
/// <returns>The HTML for a list of types based on the XML
/// documentation.</returns>

<seealso>同一个话题中的其他连接。这个标记通常没有值,仅仅有一个cref属性指向需要引用的对象名。这个属性可以是类、成员等等。
/// <seealso cref="GiveMemberListHTMLHelp"/>

XML compiler可以识别这些符号和内容,并把它转为xml文档,我将在稍后讨论这些xml文档文件。
当编译器从代码中提取xml注释的时候,任何使用<include>标识的文件都将被包含入注释,就像这些文件本来就在注释里面一样。因为编译器可以这样提取外部文件为注释,所以你可以把你的注释放到代码文件之外。不过这些注释并不很直观,因此,我从来不使用<include>。但是如果你的注释太长的话,你可以权衡一下。
<include>的几个用于指定外部文件的属性是必需的。file属性的值必须是绝对或相对文件路径,这个文件必须是一个包含xml注释的xml文件;path属性是用于指定xml注释位置的XPath路径。就像下面代码演示的:

/// <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;
   }
}
图 3 MyXMLCommentFile.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>
图3展示了MyXMLCommentFile.xml的代码。注意,我使用XPath指定注释中的位置。 这个文件实际格式是开发人员自己定的,我更倾向于这个文件跟编译器产生的文件格式是一样的。

支持标记
这里使用了11个支持标记: <c>, <code>, <list>, <listheader>, <item>, <term>, <description>, <para>, <paramref>, <see>, 和<value>.
<c> 用于标记一行代码,它通常是用在描述性文本中。
/// The source XML is loaded into the <see cref="SourceXML"/>
/// property (e.g. <c><I>obj</I>.SourceXML =
/// "<I>XML goes here</I>"</c>).

<code>用于标识一段代码,它经常用在<example>中间, <code>跟<c>很相似。不同之处在于:<code>用于一段代码,<c>用于一行代码。他们都是用于指明期间的代码文本需要保留格式。
/// <code>
/// // create the class that does translations
/// GiveHelpTransforms ght = new GiveHelpTransforms();
/// // have it load our XML into the SourceXML property
/// ght.LoadXMLFromFile(
///      "E:\\Inetpub\\wwwroot\\GiveHelp\\GiveHelpDoc.xml");
/// </code>
<list>是用于定义一个表格的注释标记。<list>的type属性可以有下面三个值:bullet、number或者 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>用于记录list 的刊头信息(看上面的例子)。最有代表性的是使用table类型的list标记。
正如你所料,<item>定义list的每一个项目,它可以单独使用或者和<term> 与 <description> 一起混用。 <term> 与 <description>是<listheader> 或者 <item>的子标记。它们通常一起使用。
<para>标记用于新的段落,他跟html的<p>非常相似。在长段的注释中,你应该用这个标记把它拆分开。
/// <summary>This is a summary.
/// <para>This is a new paragraph.</para>
/// </summary>
如果注释文本中需要特别强调一个参数,那就用<paramref>。在需要的地方插入这个标记,这个标记的作用是突出参数的名字。
/// Loads the XML documentation in the file specified by
/// <paramref>strFilePath</paramref>.
<see>作为一个超链接用在其他标记之内。它在文本中使用,通常只有一个属性cref。
/// One of the associated member functions (<see
/// cref="GiveTypeListHTMLHelp"/>,
/// <see cref="GiveMemberListHTMLHelp"/>, <see
/// cref="GiveMemberHTMLHelp"/>)
/// is called to initiate and then return the transformation.
cref 指定一个已存在的引用,更多信息看<exception>的描述。
<value>用于定义类属性,就像<remarks>定义类,<summary>定义其他成员一样。
/// <value>
/// The SourceXML property contains the XML that will be used in
/// the transformations by the member functions for this class.
/// </value>
XML文档文件
前面,我解释了在生成工程的时候,如何输出xml注释为xml文件,
其中图4展示了我生成工程同时生成xml文档的一部分内容。如果你比较图2和图4,你会注意到,图2中原始的xml注释已经被整理为图4的xml文档。
图 4 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.GiveTypeListHTMLHelp"
              />, <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>
              // create the class that does translations
              GiveHelpTransforms ght = new GiveHelpTransforms();
              // have it load our XML into the SourceXML property
              ght.LoadXMLFromFile
              ("E:\\Inetpub\\wwwroot\\GiveHelp\\GiveHelpDoc.xml");
            
              // do the translation and then write out the string
              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>

图5中,显示了需要特殊处理的标记处理后的效果。这儿罗列的所有标记几乎都是按照cref属性进行了转换。这意味着C#解析器对cref属性的原始值做了两件事:第一,他对这个可能是类或者属性的值进行了分类。第二,他处理这些值。
图 5 标记被处理的结果

Tag Result
<exception> The cref attribute is expanded
<permission> The cref attribute is expanded
<seealso> The cref attribute is expanded
<include> The reference tags are copied into the documentation file
<see> The cref attribute is expanded

例如:一个cref 值是"MyMember"的属性(这就是 cref="MyMember")。解析器会把它解析为:
M:MyProject.MyType.MyMember
M:是个分级描述,编译器把xml文档中涉及到的成员分成5类,这些分类是以文档中涉及到的成员名称的前缀命名的。图6显示了这个关系。
图 6 前缀值对应关系

Meaning When Used Prefix
Type Classes, delegates T:
Field Member variables F:
Method Procedures and functions M:
Property Properties P:
Event Events E:
Unknown A reference that cannot be qualified !:

前面例子中扩展名的其他部分也是有意义的。"MyNamespace.MyType.MyMember"这个值是一个能够被C#编译器找到的完全名称,这个名称可以使用分隔符分成几部分,每一部分都有它对应的意思:"MyNamespace" 对应名称空间,"MyType"对应类,"MyMember"对应成员。
如果cref的值不能被找到的话,编译器将用"!:" 来作为前缀。(看图7)
图 7 cref 例子

cref Value Expanded Name
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 version="1.0"?>
<doc>
  <assembly>
    <name>Name</name>
  </assembly>
  <members>
    <member name="name">
      XML Comments are here
    </member>
    ...
  </members>
</doc>

第一行是标准的头,下面是根标记<doc>。在下面是<assembly>。在<assembly> 里面是<name>。<name>是代码所生成工程的程序集名,因此通常只有一个<name> 条目。这个值通常是C#编译器提取的工程名,它与代码中的xml注释没有任何关系。
接下来是<members>,它包含每一个有联系的xml注释,每一个成员都有一个它自己的<member>。
<member>有个属性name,C# 解析器获得关联xml注释对应的成员名字作为这个属性的值。我已经描述过这个值的命名规范,剩下的由<member>所包含的xml由实际的代码文件中的xml注释所组成,前面我已经提及。

产生帮助文件
一个文档罗列所有xml注释几乎是不可能的,通常我们使用XSLT去解析XML为 Html 帮助页面。
我新建了一个名叫GiveHelp的工程,它里面包含一个简单的名叫GiveHelpTransforms 的类、几个asp.net页面和转换为html用的XSLT文件。(文章开头有源代码下载链接)。这个Web应用把本地xml文件转换为一个帮助文件。
这些asp.net页面把转换后的结果在浏览器中展现,他们使用了GiveHelpTransforms类的接口去转换xml为html。
采用GiveHelpTransforms类有两个理由:首先, 其他工程也可以利用这个代码实现从xml到html的转换;其次,在这个示范工程中,我需要一个类来演示这个转换。听起来有些混乱,但是通过我的例子演示,你就会知道该如何做。

转换

在这一节中,我假设你已经了解了XSLT,因此我只是简单的讲如何把xml文档转换为html。
我把帮助系统分作三层,最顶层是类列表。这个页面罗列xml文档中所有的类的细节信息,演示看图8。
图 8 类列表页面

Figure 8 Type List

类列表页的每一个类的链接都到一个类的所有成员列表的页面。中间层是类成员列表页,这个页面将罗列这个类的所有成员和他们的具体信息。点击一个成员链接,将进入下一层,类成员页。那里罗列类成员的细节、许可、参数、异常、例子和一些其他链接。
这一节中使用的XSLT是GiveTypeHelp.xsl文件。它在浏览器中把xml转换为罗列所有的类的html。使用一对<Dl>来创建类定义表,类的名称被放在<DT>中,详细说明将放在<DD>中,页面左边显示了类名,下面是详细介绍。
类名被从文档中萃取。你应该还记得所有的类的名字前缀是"T:" ,于是 xslt应该这样写:
<xsl:apply-templates select="./members/member[starts-with
  (@name,'T:')]" />
这一行能够在代码块的头找到<doc>元素,以后代码会被找到就像下面:
<xsl:template match="member[starts-with(@name,'T:')]">
...
</xsl:template>
这个是被<xsl:apply-templates>调用,这两个声明告诉XSLT解析器,仅仅提取满足上述条件的成员。
把每个类转换为html页,每个类的名字从name属性中获得,然后每个类的细节描述被提取。图9演示了如何利用<DT> 和 <DD>处理
图 9 提取类的名字和细节
<DT>
<A>
    <xsl:attribute name="href">
         GiveTypeMemberListHelp.aspx?Type=<xsl:value-of select="substring-
         after(@name,':')" />
    </xsl:attribute>
    <B>
      <xsl:value-of select="substring-after(@name,'.')" />
    </B>
</A>
</DT>
<DD>
  <xsl:apply-templates select="remarks | summary" />
</DD>
在<DT>中,一个指向具体类信息的超链接被创建。在<DD>中,<remarks> 或者 <summary>值被提取。下一节我将解释XSLT做了那些处理,图8就是显示了处理结果。

成员 列表

我的例子中,在类列表那一页面,如果一个类被点击,页面将跳转到GiveTypeMemberListHelp.aspx页面,这个页面罗列了该类的所有成员。 图 10 是对XSLT的一段摘录,更详细的看源代码。
图 10 GiveTypeMemberListHelp.xsl
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  version="1.0">
  <xsl:output method="html" />
  <xsl:param name="WhichType" />
  <xsl:template match="doc">
    <xsl:apply-templates select="./assembly/name" />
    <H2>
      Members
    </H2>
    <xsl:apply-templates select="./members/member[starts-
      with(@name,concat('F:',$WhichType))]" mode="fields" />
    <xsl:apply-templates select="./members/member[starts- 
      with(@name,concat('P:',$WhichType))]" mode="properties" /> 
    <xsl:apply-templates select="./members/member[starts-
      with(@name,concat('M:',$WhichType))]" mode="methods" /> 
    <xsl:apply-templates select="./members/member[starts-
      with(@name,concat('E:',$WhichType))]" mode="events" /> 
  </xsl:template>
  <xsl:template match="name">
    <TABLE width="100%" border="1" cellspacing="0" cellpadding="10" 
      bgcolor="lightskyblue">
      <TR>
        <TD align="center">
          <H1>
            <xsl:text>Assembly: </xsl:text>
            <xsl:value-of select="." />
            <BR />
            <xsl:text>Type: </xsl:text>
            <xsl:value-of select="substring-after($WhichType,'.')" />
          </H1>
        </TD>
      </TR>
    </TABLE>
  </xsl:template>
  <xsl:template match="member" mode="methods">
    <xsl:if test="position() = 1">
      <P></P>
      <H3>Methods</H3>
    </xsl:if>
    <DL>
      <DT>
        <A>
          <xsl:attribute name="href">
            GiveTypeMemberHelp.aspx?
            Member=<xsl:value-of select="substring-after(@name,':')" />
          </xsl:attribute>
          <B>
            <xsl:value-of select="substring-
              after(@name,concat($WhichType,'.'))" />
          </B>
        </A>
      </DT>
      <DD>
        <xsl:apply-templates select="remarks | summary" />
      </DD>
    </DL>
    <P></P>
  </xsl:template>
...
当GiveTypeMemberListHelp.aspx 页面被请求的时候,它接受了一个完全的类名GET参数。(比如:/GiveTypeMemberListHelp.aspx?Type=MyNamespace.MyType)在XML-to-HTML的转换过程中,XSLT把这个值传递给XSLT的参数:WhichType。这个XSLT的参数如下:
<xsl:param name="WhichType" />
这个参数用在过滤 <member>。下面是XSLT的具体做法:
<xsl:apply-templates select="./members/member[starts-
  with(@name,concat('F:',$WhichType))]" mode="fields" />
<xsl:apply-templates select="./members/member[starts-
  with(@name,concat('P:',$WhichType))]" mode="properties" />
<xsl:apply-templates select="./members/member[starts-
  with(@name,concat('M:',$WhichType))]" mode="methods" />
<xsl:apply-templates select="./members/member[starts-
  with(@name,concat('E:',$WhichType))]" mode="events" />
  
代码很明确,它是调用<member>的 Fields, Properties, Methods 和 Events 模板。我使用XPath 来实现这个功能,Xpath的参数就如下:
*:Namespace.Type.Member
每个成员的调用模板都很相似,我来解释一下GiveTypeMemberListHelp.xsl 中的模板(看图10)。 <xsl:if> 这个标记用来判断是不是满足条件,如果满足,将被写到 <DT>中 间的部分。 <DD>中间的部分是来自于<summary> 或 <remarks>的 部分数据.

类成员

这一节的讨论在 GiveTypeMemberHelp.xsl 文件中。就像类成员列表那一层,类成员层也有一个参数,这个参数就是类成员的全名。
<xsl:param name="WhichMember" />
类可以从类成员全名中获得,于是你可以看到下面的XSLT:
<xsl:param name="WhichType" select="concat
(concat( substring-before($WhichMember,'.'),  '.'),
substring-before(substring-after($WhichMember,'.'),'.'))" />
XSLT 的开头部分要比上一个要复杂些:
<xsl:template match="doc">
  <xsl:apply-templates select="./assembly/name" />
  <xsl:apply-templates select="./members/member[contains
  (@name,$WhichMember)]" />
</xsl:template>
第一个<xsl:apply-templates> 是从<name>中提取数据;第二个<xsl:apply-templates> 是提取满足$WhichMember的类成员。下面这一行是从图11中提取的处理成员注释的部分。
<xsl:template match="member[contains(@name,$WhichMember)]">
图 11 GiveTypeMemberHelp.xsl 摘录
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="html" />
    <xsl:param name="WhichMember" />
    <xsl:param name="WhichType" select="concat(concat( substring-
      before($WhichMember,'.'),  '.'), substring-before(substring-
      after($WhichMember,'.'),'.'))" />
    <xsl:template match="doc">
        <xsl:apply-templates select="./assembly/name" />
        <xsl:apply-templates select="./members/
          member[contains(@name,$WhichMember)]" />
    </xsl:template>
    <xsl:template match="name">
...
    </xsl:template>
    <xsl:template match="member[contains(@name,$WhichMember)]">
        <H2>
            <xsl:choose>
                <xsl:when test="starts-with(@name,'M:')">
                    Method
                </xsl:when>
                <xsl:when test="starts-with(@name,'F:')">
                    Field
                </xsl:when>
                <xsl:when test="starts-with(@name,'P:')">
                    Property
                </xsl:when>
                <xsl:when test="starts-with(@name,'E:')">
                    Event
                </xsl:when>
            </xsl:choose>
        </H2>
        <TABLE width="100%" border="1" cellspacing="0" cellpadding="10" 
          bgcolor="silver">
            <TR>
                <TD>
                    <B>
                        <xsl:value-of select="substring-
                          after($WhichMember,concat($WhichType,'.'))" />
                    </B>
                </TD>
            </TR>
        </TABLE>
        <P></P>
        <B>Description:</B>
        <DL>
            <xsl:apply-templates select="remarks" />
        </DL>
        <DL>
            <xsl:apply-templates select="summary" />
        </DL>
        <DL>
            <xsl:apply-templates select="value" />
        </DL>
        <B>Permission:</B>
        <DL>
            <xsl:apply-templates select="permission" />
        </DL>
        <xsl:if test="not(starts-with(@name,'F:'))">
            <B>Parameters:</B>
            <DL>
                <xsl:apply-templates select="param" />
            </DL>
            <xsl:if test="not(starts-with(@name,'E:')) and not(starts-
              with(@name,'P:'))">
                <B>Returns:</B>
                <DL>
                    <xsl:apply-templates select="returns" />
                </DL>
            </xsl:if>
            <B>Exceptions:</B>
            <DL>
                <xsl:apply-templates select="exception" />
            </DL>
        </xsl:if>
        <B>Example:</B>
        <DL>
            <xsl:apply-templates select="example" />
        </DL>
        <B>See Also:</B>
        <DL>
            <xsl:apply-templates select="seealso" />
        </DL>
    </xsl:template>
    <xsl:template match="summary | remarks | value">
        <DT></DT>
        <DD>
            <xsl:apply-templates />
        </DD>
    </xsl:template>
    ...
    <xsl:template match="*">
        <xsl:copy>
            <xsl:apply-templates />
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>
...
如果你对XSLT比较熟悉,这些都是很简单的。
除了插入不同的标记以外,属于这个成员的标记也会被处理。他们是通过<xsl:apply-templates select="summary" /> 和 <xsl:apply-templates select= "param" />来处理的。你可以在GiveTypeMemberHelp.xsl中找到代码。

处理标记

处理完文档的标记之后,开始处理xml注释,这个处理过程在工程中随处可见。
在不同的处理中,主标记会分别不同。 在类列表和类成员列表中,它是<summary> 和 <remarks>。在类成员层,它是primary tag 你可以在XSLT文件中看到: <xsl:apply-templates select="primary tag" "primary tag" 被作为主标记。
一旦被执行,就像下面,进程会跳转到对应块。
<xsl:template match="primary tag">
...
    <xsl:apply-templates />
...
</xsl:template>


在primary tag 附近的html标记将通过<xsl:apply-templates />来创建。

这些支持的标记在转换中通常被转为html标记。例如<c> 就是做下面的转换。

<xsl:template match="c">
  <CODE>
    <xsl:apply-templates />
  </CODE>
</xsl:template>
Xml注释中的 <c> 对应 html 中的 <CODE>。 更多的对应关系看图12。
图 12 XML 注释标签与HTML对应关系
.NET XML Comment Tag My HTML Equivalent
<c> <CODE>
<para> <P>
<paramref> <I>
<see> <A>
<list type="table"> <TABLE>
<list type="bullet"> <UL>
<list type="number"> <OL>
<listheader> (with table) <THEAD>
<listheader> (with list) <LI><B>
<item> (with table and in header) <TR>[<TH>]
<item> (with table) <TR>[<TD>]
<item> (with list) <LI>
<term> (with table and in header) <TH>
<term> (with table) <TD>
<term> (with list) Special: adds "-" to end of tag value
<description> (with table and in header ) <TH>
<description> (with table) <TD>
<description> (with list) None
<code> <PRE>
* Special: all other nodes are just copied in

图13演示了xml(图4)通过XSLT(图11中的XSLT)转换为html。
图 13 转换为html后的GiveHelpDoc.xml
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- saved from url=(0122)http://localhost/GiveHelp/
GiveTypeMemberHelp.aspx?Member=GiveHelp.GiveHelpTransforms.
  GiveMemberListHTMLHelp(System.String) -->
<HTML>
  <HEAD>
      <META http-equiv="Content-Type" content="text/html; charset=utf-8">
      <META content="MSHTML 6.00.2462.0" name="GENERATOR">
      <META content="C#" name="CODE_LANGUAGE">
      <META content="JavaScript (ECMAScript)" 
        name="vs_defaultClientScript">
      <META content="http://schemas.microsoft.com/intellisense/ie5" 
        name="vs_targetSchema">
  </HEAD>
  <BODY>
      <TABLE cellSpacing="0" cellPadding="10" width="100%" 
        bgColor="lightskyblue" border="1">
          <TBODY>
              <TR>
                <TD align="middle">
                      <H1>
                          Assembly: GiveHelp
                          <BR>
                          Type: GiveHelpTransforms
                      </H1>
                  </TD>
              </TR>
          </TBODY>
      </TABLE>
      <H2>
          Method
      </H2>
      <TABLE cellSpacing="0" cellPadding="10" width="100%" 
        bgColor="silver" border="1">
          <TBODY>
              <TR>
                  <TD>
                      <B>GiveMemberListHTMLHelp(System.String)</B>
                  </TD>
              </TR>
          </TBODY>
      </TABLE>
      <P>
      </P>
      <B>Description:</B>
      <DL>
      </DL>
      <DL>
          <DT>
              <DD>
                  Calling this function will take the XML in 
                    <A href="http://localhost/GiveHelp/   
                      GiveTypeMemberHelp.aspx?Type=GiveHelp.
                      GiveHelpTransforms&amp;Member=GiveHelp.
                      GiveHelpTransforms.SourceXML"> SourceXML</A>
                      and translate it to a list of 
                      Members in the specified type.
              </DD>
          </DT>
      </DL>
      <DL>
      </DL>
      <B>Permission:</B>
      <DL>
          <DT>
              <DD>
                  <DL>
                      <DT><A href="http://localhost/GiveHelp/
                           GiveTypeHelp.aspx?Type=."></A>
                          <DD>
                              public
                          </DD>
                      </DT>
                  </DL>
              </DD>
          </DT>
      </DL>
      <B>Parameters:</B>
      <DL>
          <DT>
              <DD>
                  <DL>
                      <DT><I>strType</I>
                          <DD>
                              The fully qualified name of the type that 
                              the member is in.
                          </DD>
                  </DL>
              </DD>
      </DL>
      <B>Returns:</B>
      <DL>
          <DT>
              <DD>
                  The HTML that lists the types that are in the 
                  XML documentation.
              </DD>
          </DT>
      </DL>
      <B>Exceptions:</B>
      <DL>
      </DL>
      <B>Example:</B>
      <DL>
          <DT>
              <DD>
                  <PRE> // create the class that does translations
          
          GiveHelpTransforms ght = new GiveHelpTransforms();
          // have it load our XML into the SourceXML property
          ght.LoadXMLFromFile(
                  "E:\\Inetpub\\wwwroot\\GiveHelp\\GiveHelpDoc.xml");
            
          // do the translation and then write out the string
          Response.Write( 
              ght.GiveMemberListHTMLHelp(Request.QueryString.Get("Type")));
          </PRE>
        </DD>
      </DT>
      </DL>
      <B>See Also:</B>
      <DL>
          <DT>
              <DD>
                  <A href="http://localhost/GiveHelp/     
                  GiveTypeMemberHelp.aspx?Type=GiveHelp.
                  GiveHelpTransforms&amp;Member=GiveHelp.GiveHelpTransforms.
                  GiveTypeListHTMLHelp">
                  GiveHelpTransforms.GiveTypeListHTMLHelp</A> <DT></DT>
                  <DD>
                      <A href="http://localhost/GiveHelp/    
                      GiveTypeMemberHelp.aspx?Type=GiveHelp.
                      GiveHelpTransforms&amp;Member=GiveHelp.
                      GiveHelpTransforms.GiveMemberHTMLHelp(System.String)">
                      GiveHelpTransforms.GiveMemberHTMLHelp(System.String)
                      </A>
                  </DD>
              </DD>
          </DT>
      </DL>
  </BODY>
</HTML>
总结 虽然很早就已经有工具可以从源代码中提取注释,但是他们都没有被广泛的应用。原因大多都是使用复杂或者没有跟主流开发工具集成在一起。 C#中基于XML的注释,把语言跟工具很
分享到:
评论

相关推荐

    Visual Studio .NET Tips and Tricks.pdf

    在Visual Studio .NET中,通过键入`///`(对于C#)或`'`(对于VB.NET),可以自动插入一个XML注释模板,方便快速编写注释。 #### 注释网页 在编辑ASP.NET Web页面时,可以使用`&lt;%-- --%&gt;`来添加注释,这些注释只...

    30个c#源代码小程序,实例很实用,是visual Studio.net学习的精品。

    本资源包含30个C#源代码小程序,是针对Visual Studio .NET初学者或希望巩固C#编程技能的开发者精心准备的学习材料。 这些小程序涵盖了C#语言的基础和进阶特性,包括但不限于: 1. **控制结构**:如条件语句(if-...

    VS c# 生成 文档 注释 源代码

    【标题】:“VS c# 生成 文档 注释 源代码”涉及到的是在Visual Studio (VS) 使用C#编程语言时如何自动生成并处理XML文档注释的过程。这一过程对于编写高质量、易于理解的代码至关重要,特别是对于团队合作和API文档...

    C#根据注释生成文档

    通过集成到构建系统(如MSBuild或Visual Studio的Pre/Post-Build事件)中,每次编译代码时自动更新XML注释文件,然后触发文档生成。 总结起来,通过在C#代码中添加XML注释,并利用如Sandcastle这样的工具,我们可以...

    Microsoft Visual Studio 2022 自定义模板注释 C#类文件、Web相关文件、WinForm相关文件

    这些注释可以按照约定俗成的格式编写,例如使用三重斜线(///)来创建XML注释,这将在生成文档或代码提示时提供帮助。模板可能包含以下元素: 1. 类名注释:描述类的主要功能和用途。 2. 属性注释:说明属性的作用...

    将C# 类文件中属性和方法自动生成文档,C#类文档结构化生成

    C#支持XML注释,它允许程序员在代码中添加结构化的注释,这些注释可以被工具转换为格式化的文档。例如,对于一个方法,你可以这样添加注释: ```csharp /// /// 这里是对方法的简短描述 /// /// 参数名的描述 ///...

    vs c# 文档注释 生成 源码

    这样,在编译项目时,VS会根据源代码中的三斜线注释(///)生成一个XML文件,其中包含了所有公共类型的API文档。 生成的XML文件可以用于创建API参考文档,例如通过Sandcastle、docfx等工具。但这个描述中提到的是一...

    .Net十大必备工具之一

    它还可通过反射处理汇编源代码,并在代码中使用XML注释生成MSDN形式的文档,这种文档比难以解读的XML更易于理解。 5. Nunit NUnit是一个.NET上的单元测试框架。NUnit 1.x主要是移植JUnit 3.8。从2.0版本开始,NUnit...

    visual studio 2012常用官方插件

    5. StyleCop:StyleCop检查C#源代码文件,确保代码遵循Microsoft的编码风格和格式规范。它可以自动检测并修正常见的编码错误和风格问题。 6. Visual Studio Color Theme Editor:此插件允许用户自定义Visual Studio...

    Doc.Net:用于Visual Studio的文档扩展插件,用于创建从其他项目中获取源代码和文本标记文件并将其编译为帮助文件的项目

    Doc.Net是一款专为Visual Studio设计的文档扩展插件,它极大地简化了开发人员在软件开发过程中生成专业帮助文件的工作流程。这款插件的核心功能在于,它能够从项目的源代码和文本标记文件中提取信息,并自动编译成...

    C#文件信息查看器源代码

    8. XML文档注释:源代码中可能会有XML文档注释,用于生成帮助文档或代码自动生成工具,如Visual Studio的IntelliSense。 9. 编译与调试:开发过程中,C#项目使用Visual Studio IDE,其强大的调试工具可以帮助开发者...

    .net 代码行数统计工具

    1. **全面统计**:工具会遍历整个解决方案或指定的代码目录,统计所有.NET语言(如C#、VB.NET、F#等)的源代码文件,计算总行数。 2. **分类统计**:除了总行数,工具还会区分不同类型行,如: - 实际代码行:不...

    C# 编写的 Xml 编辑器

    【C# 编写的Xml编辑器】是一款基于Visual Studio 2010开发的软件项目,主要用于XML文档的创建、查看和编辑。由于项目原始工程可能适用于VS2008,如果你使用的开发环境是VS2008,则需要自行调整工程设置以使其兼容。...

    ndoc for .net 2.0

    1. **XML注释解析**:`ndoc`支持C#语言中的XML注释,通过分析源代码中的这些注释,提取出类、方法、属性等元素的说明。 2. **模板驱动**:`ndoc`允许用户自定义文档输出样式,通过使用不同的模板,可以生成符合各种...

    .Net十大必备工具之二

    它还可通过反射处理汇编源代码,并在代码中使用XML注释生成MSDN形式的文档,这种文档比难以解读的XML更易于理解。 5. Nunit NUnit是一个.NET上的单元测试框架。NUnit 1.x主要是移植JUnit 3.8。从2.0版本开始,NUnit...

    C#去代码注释

    这样的工具在保持代码清晰的同时,可以帮助减小源代码文件的大小,方便分发和存储。 在压缩包文件中,我们可以看到以下文件: 1. `Form1.cs` 和 `Form2.cs`:这是两个Windows Forms应用程序的界面代码文件,很可能...

    vs注释生成chm帮助文档工具和详细说明书

    1. **代码注释规范**:为了生成有效的CHM文档,你需要确保你的代码注释符合某种规范,如XML注释(例如C#的`///`),这样工具才能正确解析并生成对应的文档条目。 2. **配置工具**:工具可能需要配置,例如指定输入...

    创建帮助文件(C#)NDOC.

    这个过程涉及到将C#源代码中的XML注释转换成用户友好的HTML或其他格式的文档。 描述中提到的"根据vs生成的dll和xml自动创建帮助文档",是指在Visual Studio(VS)环境下,通过编译C#项目时会生成一个XML文件,这个...

    C#源代码格式化插件

    【C#源代码格式化插件】是一款专为Visual Studio用户设计的实用工具,旨在提升开发者们的编码效率和代码整洁度。此插件兼容Visual Studio 2008及2010版本,只需在配置文件中简单调整,就能适应不同版本的IDE环境。其...

    vs注释文档生成工具

    "vs注释文档生成工具"就是这样一个专门针对Visual Studio(VS)开发环境的辅助工具,它能自动从源代码中的注释提取信息,生成易于阅读的文档,极大地减轻了程序员在项目后期编写文档的工作负担。 **描述详解:** ...

Global site tag (gtag.js) - Google Analytics