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

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

作者: J. Andrew Schafer
这篇文章假设你对 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 将自动插入注释标记,开发人员只需要增加些额外的标记和值。下面就是在成员函数前增加三个斜线,自动增加的注释:

///
1<summary>
2    ///
3    /// </summary>

///

1<param name="strFilePath"/>

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
{
    ///
 1<remarks>
 2        /// Class that contains functions to do transformations to help files.
 3        /// The source XML is loaded into the <see cref="SourceXML"></see> property 
 4        /// (e.g. <c><i>obj</i>.SourceXML = "<i>XML goes here</i>"</c>). One of 
 5        /// the associated member functions (<see cref="GiveTypeListHTMLHelp">, <see cref="GiveMemberListHTMLHelp"></see>, <see cref="GiveMemberHTMLHelp"></see>) 
 6        /// is called to initiate and then return the transformation.
 7        /// <para>
 8        /// <list type="table">
 9        /// <listheader>
10        /// <term>Help Page</term>
11        /// <description>Function to call</description>
12        /// </listheader>
13        /// <item><term>List of Types</term>
14        /// <description>GiveTypeListHTMLHelp</description></item>
15        /// <item><term>List of members</term>
16        /// <description>GiveMemberListHTMLHelp</description></item>
17        /// <item><term>Help for a single member</term>
18        /// <description>GiveMemberHTMLHelp</description></item>
19        /// </list>
20        /// </para>
21        /// </see></remarks>

///

1<permission cref="">public</permission>

///

 1<example><code>
 2        /// // create the class that does translations
 3        /// GiveHelpTransforms ght = new GiveHelpTransforms();
 4        /// // have it load our XML into the SourceXML property
 5        /// ght.LoadXMLFromFile("E:\\Inetpub\\www-
 6        /// root\\GiveHelp\\GiveHelpDoc.xml");
 7        ///
 8        /// // do the translation and then write out the string
 9        /// Response.Write( ght.
10        /// GiveMemberHTMLHelp(Request.QueryString.Get("Type"),
11        /// Request.QueryString.Get("Member")) );
12        /// </code></example>

public class GiveHelpTransforms { ???

        ///
1<summary>
2            /// Calling this function will take the XML in <see cref="SourceXML"></see> 
3            /// and translate it to a list of Members in the specified type.
4            /// </summary>

///

1<param name="strType"/>

The fully qualified name of the type that /// the member is in. ///

1<returns>The HTML that lists the types that are in the XML 
2            /// documentation.</returns>

///

1<seealso cref="GiveTypeListHTMLHelp"></seealso>

///

1<seealso cref="GiveMemberHTMLHelp"></seealso>

///

 1<example><code>
 2            /// // create the class that does translations
 3            /// GiveHelpTransforms ght = new GiveHelpTransforms();
 4            /// // have it load our XML into the SourceXML property
 5            /// ght.LoadXMLFromFile(
 6            ///            "E:\\Inetpub\\wwwroot\\GiveHelp\\GiveHelpDoc.xml");
 7            ///
 8            /// // do the translation and then write out the string
 9            /// Response.Write( 
10            /// ght.GiveMemberListHTMLHelp(Request.
11            /// QueryString.Get("Type")));
12            /// </code></example>

///

1<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();
        }
   }     

这里有九个主标记:

  1<remarks>, <summary>, <example>, <exception>, <param/>, <permission>, <returns>, <seealso>, <include>.   
  2在上下文中,<remarks>描述了这个类的细节。   
  3
  4    
  5    
  6    /// <remarks>
  7    /// Class that contains functions to do
  8    /// transformations to help files.
  9    /// </remarks>
 10    
 11    
 12
 13C#文档推荐使用<remarks>描述一个对象,<summary>描述一个对象成员。奇怪的是如果你用Visual Studio .NET IDE在一个类前输入///,他将自动增加<summary>,而不是<remarks> ,这时你需要手工输入<remarks>。   
 14<summary>经常在C#文档中发现,这个标记用于描述类的成员,包括属性、方法、成员等。   
 15
 16    
 17    
 18    /// <summary>
 19    /// This XmlDocument based attribute contains the
 20    /// XML documentation for the project.
 21    /// </summary>
 22<summary>也可以很好的描述对象,但是不推荐,xml注释文档推荐使用<remarks>描述对象。   
 23<example>被用来演示如何使用,例子可以使任何正确的文本文件,但是更多是一小段代码:   
 24
 25    
 26    
 27    /// <example>
 28    /// <code>
 29    /// // create the class that does translations
 30    /// GiveHelpTransforms ght = new GiveHelpTransforms();
 31    /// // have it load our XML into the SourceXML property
 32    /// ght.LoadXMLFromFile(
 33    ///  "E:\\Inetpub\\wwwroot\\GiveHelp\\GiveHelpDoc.xml");
 34    /// </code>
 35    /// </example>
 36    
 37    
 38
 39如果其中有代码,<code>经常被使用,<code>是将要被支持的一个标记。   
 40<exception>处理异常文档。如果多于一个异常会被抛出,你就需要多个<exception> <exception>有一个属性:cref 这个属性值是将被扔出异常的类名。这个类名必须是正确的,因为C#编译器将检查这一项,后面我将具体讨论。   
 41这篇文章的代码将不会扔出任何异常,但是下面有个代码例子,演示如何使用<exception>   
 42
 43    
 44    
 45    /// <exception cref="SampleException">
 46    /// Normally, a discussion on any exceptions thrown would go here.
 47    /// </exception>
 48<param/>用于描述方法、属性的参数。当你在一个方法前面输入/// 他会被自动的插入。<param/>有个属性,name 用于记录源代码中参数的名字。   
 49
 50    
 51    
 52    /// <param name="strFilePath"/>The path to the file containing the
 53    /// source XML.
 54    
 55    
 56
 57类成员访问修饰符是通过 <permission>来记录,它的值用来写明访问限制,值不是必需的。值可以是下面几种:public, private, protected等。Scope 是另外一个标记,用于表示是不是static的方法。   
 58<permission>有个属性crefcref 属性的值用来表示调用当前编译环境的一个类成员或者属性,它通常在System.Security.PermissionSet中定义。   
 59
 60    
 61    
 62    /// <permission cref="System.Security.PermissionSet">Public
 63    /// Access</permission>
 64<returns> <param/> 相类似,他用来描述方法、属性的返回值。   
 65
 66    
 67    
 68    /// <returns>The HTML for a list of types based on the XML
 69    /// documentation.</returns>
 70<seealso>同一个话题中的其他连接。这个标记通常没有值,仅仅有一个cref属性指向需要引用的对象名。这个属性可以是类、成员等等。   
 71
 72    
 73    
 74    /// <seealso cref="GiveMemberListHTMLHelp"></seealso>
 75    
 76
 77XML compiler可以识别这些符号和内容,并把它转为xml文档,我将在稍后讨论这些xml文档文件。   
 78当编译器从代码中提取xml注释的时候,任何使用<include>标识的文件都将被包含入注释,就像这些文件本来就在注释里面一样。因为编译器可以这样提取外部文件为注释,所以你可以把你的注释放到代码文件之外。不过这些注释并不很直观,因此,我从来不使用<include>。但是如果你的注释太长的话,你可以权衡一下。   
 79<include>的几个用于指定外部文件的属性是必需的。file属性的值必须是绝对或相对文件路径,这个文件必须是一个包含xml注释的xml文件;path属性是用于指定xml注释位置的XPath路径。就像下面代码演示的:   
 80
 81    
 82    
 83    /// <include file="MyXMLCommentFile.xml" path='doc/members/member[@name="T:MyExampleClass"]/*'></include>
 84    public class MyExampleClass
 85    {
 86    /// <include file="MyXMLCommentFile.xml" path='doc/members/member[@name="M:MyExampleMethod"]/*'></include>
 87       public string MyExampleMethod(string strReturnThis)
 88       {
 89         return strReturnThis;
 90       }
 91    }
 92    
 93
 94 3  MyXMLCommentFile.xml    
 95
 96    
 97    
 98    <doc>
 99<members>
100<member name="T:MyExampleClass">
101<remarks>
102          This is my example class that does <b>nothing</b>.
103          </remarks>
104</member>
105<member name="M:MyExampleMethod">
106<summary>
107          This method just returns a string.
108          </summary>
109<param name="strReturnThis"/>
110          This is a string parameter that will just be returned. 
111          
112          <returns>
113          Just returns the input parameter.
114          </returns>
115</member>
116</members>
117</doc>
118    
119
120图3展示了MyXMLCommentFile.xml的代码。注意,我使用XPath指定注释中的位置。 这个文件实际格式是开发人员自己定的,我更倾向于这个文件跟编译器产生的文件格式是一样的。   
121  
122** 支持标记  **   
123这里使用了11个支持标记: <c>, <code>, <list>, <listheader>, <item>, <term>, <description>, <para>, <paramref>, <see>, <value>.   
124<c> 用于标记一行代码,它通常是用在描述性文本中。   
125
126    
127    
128    /// The source XML is loaded into the <see cref="SourceXML"></see>
129    /// property (e.g. <c><i>obj</i>.SourceXML =
130    /// "<i>XML goes here</i>"</c>).
131    
132    
133
134<code>用于标识一段代码,它经常用在<example>中间, <code><c>很相似。不同之处在于:<code>用于一段代码,<c>用于一行代码。他们都是用于指明期间的代码文本需要保留格式。   
135
136    
137    
138    /// <code>
139    /// // create the class that does translations
140    /// GiveHelpTransforms ght = new GiveHelpTransforms();
141    /// // have it load our XML into the SourceXML property
142    /// ght.LoadXMLFromFile(
143    ///      "E:\\Inetpub\\wwwroot\\GiveHelp\\GiveHelpDoc.xml");
144    /// </code>
145<list>是用于定义一个表格的注释标记。<list>type属性可以有下面三个值:bulletnumber或者 table<list>内有些其他的标记,就如下面代码演示的:   
146
147    
148    
149    /// <list type="table">
150    /// <listheader>
151    /// <term>Help Page</term>
152    /// <description>Function to call</description>
153    /// </listheader>
154    /// <item><term>List of Types</term>
155    /// <description>GiveTypeListHTMLHelp</description></item>
156    /// <item><term>List of members</term>
157    /// <description>GiveMemberListHTMLHelp</description></item>
158    /// <item><term>Help for a single member</term>
159    /// <description>GiveMemberHTMLHelp</description></item>
160    /// </list>
161<listheader>用于记录list 的刊头信息(看上面的例子)。最有代表性的是使用table类型的list标记。   
162正如你所料,<item>定义list的每一个项目,它可以单独使用或者和<term>  <description> 一起混用。 <term>  <description><listheader> 或者 <item>的子标记。它们通常一起使用。   
163<para>标记用于新的段落,他跟html<p>非常相似。在长段的注释中,你应该用这个标记把它拆分开。   
164
165    
166    
167    /// <summary>This is a summary.
168    /// <para>This is a new paragraph.</para>
169    /// </summary>
170    
171
172如果注释文本中需要特别强调一个参数,那就用<paramref>。在需要的地方插入这个标记,这个标记的作用是突出参数的名字。   
173
174    
175    
176    /// Loads the XML documentation in the file specified by
177    /// <paramref>strFilePath</paramref>.
178    
179
180<see>作为一个超链接用在其他标记之内。它在文本中使用,通常只有一个属性cref   
181
182    
183    
184    /// One of the associated member functions (<see cref="GiveTypeListHTMLHelp"></see>,
185    /// <see cref="GiveMemberListHTMLHelp"></see>, <see cref="GiveMemberHTMLHelp"></see>)
186    /// is called to initiate and then return the transformation.
187    
188
189cref 指定一个已存在的引用,更多信息看<exception>的描述。   
190<value>用于定义类属性,就像<remarks>定义类,<summary>定义其他成员一样。   
191
192    
193    
194    /// <value>
195    /// The SourceXML property contains the XML that will be used in
196    /// the transformations by the member functions for this class.
197    /// </value>
198    
199
200** XML文档文件    
201** 前面,我解释了在生成工程的时候,如何输出xml注释为xml文件,   
202其中图4展示了我生成工程同时生成xml文档的一部分内容。如果你比较图2和图4,你会注意到,图2中原始的xml注释已经被整理为图4的xml文档。   
203 4  GiveHelpDoc.xml 部分代码    
204
205    
206    
207    <?xml version="1.0"?>
208<doc>
209<assembly>
210<name>GiveHelp</name>
211</assembly>
212<members>
213<member name="T:GiveHelp.GiveHelpTransforms">
214<remarks>
215                  Class that contains functions to do transformations to help 
216                  files. The source XML is loaded into the <see cref="P:GiveHelp.GiveHelpTransforms.SourceXML"></see> property 
217                  (e.g. <c><i>obj</i>.SourceXML = "<i>XML goes here</i>"
218                  </c>). One of the associated member functions (<see cref="M:GiveHelp.GiveHelpTransforms.GiveTypeListHTMLHelp"></see>, <see cref="M:GiveHelp.GiveHelpTransforms.
219                  GiveMemberListHTMLHelp(System.String)"></see>, <see cref="M:GiveHelp.GiveHelpTransforms.GiveMemberHTMLHelp
220                  (System.String)"></see>) is called to initiate and then return 
221                  the transformation.
222                    <para>
223<list type="table">
224<listheader>
225<term>Help Page</term>
226<description>Function to call</description>
227</listheader>
228<item><term>List of Types</term>
229<description>GiveTypeListHTMLHelp</description></item>
230<item><term>List of members</term>
231<description>GiveMemberListHTMLHelp</description>
232</item>
233<item><term>Help for a single member</term>
234<description>GiveMemberHTMLHelp</description></item>
235</list>
236</para>
237</remarks>
238<permission cref="!:">public</permission>
239<example><code>
240                  // create the class that does translations
241                  GiveHelpTransforms ght = new GiveHelpTransforms();
242                  // have it load our XML into the SourceXML property
243                  ght.LoadXMLFromFile
244                  ("E:\\Inetpub\\wwwroot\\GiveHelp\\GiveHelpDoc.xml");
245                
246                  // do the translation and then write out the string
247                  Response.Write( 
248                  ght.GiveMemberHTMLHelp(Request.QueryString.Get("Type"),
249                  Request.QueryString.Get("Member")) );
250                </code></example>
251</member>
252            ???
253            <member name="P:GiveHelp.GiveHelpTransforms.SourceXML">
254<value>
255                 The SourceXML property contains the XML that will be used in 
256                 the transformations by the member functions for this class.
257                </value>
258<permission cref="!:">public</permission>
259</member>
260</members>
261</doc>
262    
263
264  
265图5中,显示了需要特殊处理的标记处理后的效果。这儿罗列的所有标记几乎都是按照cref属性进行了转换。这意味着C#解析器对cref属性的原始值做了两件事:第一,他对这个可能是类或者属性的值进行了分类。第二,他处理这些值。   
266 5  标记被处理的结果    
267  
268Tag   
269|  Result   
270  
271---|---  
272<exception>   
273|  The cref attribute is expanded   
274  
275<permission>   
276|  The cref attribute is expanded   
277  
278<seealso>   
279|  The cref attribute is expanded   
280  
281<include>   
282|  The reference tags are copied into the documentation file   
283  
284<see>   
285|  The cref attribute is expanded   
286  
287  
288例如:一个cref 值是"MyMember"的属性(这就是 cref="MyMember")。解析器会把它解析为:   
289
290    
291    
292    M:MyProject.MyType.MyMember
293    
294
295M:是个分级描述,编译器把xml文档中涉及到的成员分成5类,这些分类是以文档中涉及到的成员名称的前缀命名的。图6显示了这个关系。   
296 6  前缀值对应关系    
297  
298Meaning   
299|  When Used   
300|  Prefix   
301  
302---|---|---  
303Type   
304|  Classes, delegates   
305|  T:   
306  
307Field   
308|  Member variables   
309|  F:   
310  
311Method   
312|  Procedures and functions   
313|  M:   
314  
315Property   
316|  Properties   
317|  P:   
318  
319Event   
320|  Events   
321|  E:   
322  
323Unknown   
324|  A reference that cannot be qualified   
325|  !:   
326  
327  
328前面例子中扩展名的其他部分也是有意义的。"MyNamespace.MyType.MyMember"这个值是一个能够被C#编译器找到的完全名称,这个名称可以使用分隔符分成几部分,每一部分都有它对应的意思:"MyNamespace" 对应名称空间,"MyType"对应类,"MyMember"对应成员。   
329如果cref的值不能被找到的话,编译器将用"!:" 来作为前缀。(看图7)   
330 7  cref 例子    
331  
332cref Value   
333|  Expanded Name   
334  
335---|---  
336cref="MyClass"   
337|  "T:MyProject.MyClass"   
338  
339cref="MyField"   
340|  "F:MyProject.MyClass.MyField"   
341  
342cref="MyMethod"   
343|  "M:MyProject.MyClass.MyMethod"   
344  
345cref="MyProperty"   
346|  "E:MyProject.MyClass.MyProperty"   
347  
348cref="MyEvent"   
349|  "P:MyProject.MyClass.MyEvent"   
350  
351cref="MyUnknown"   
352|  "!:MyUnknown"   
353  
354  
355cref 的属性不仅在上面的解码中用,它的成员名在转换为xml文档中也被使用,就像你下面要看到的。   
356
357    
358    
359    <?xml version="1.0"?>
360<doc>
361<assembly>
362<name>Name</name>
363</assembly>
364<members>
365<member name="name">
366          XML Comments are here
367        </member>
368        ...
369      </members>
370</doc>
371    
372
373第一行是标准的头,下面是根标记<doc>。在下面是<assembly>。在<assembly> 里面是<name><name>是代码所生成工程的程序集名,因此通常只有一个<name> 条目。这个值通常是C#编译器提取的工程名,它与代码中的xml注释没有任何关系。   
374接下来是<members>,它包含每一个有联系的xml注释,每一个成员都有一个它自己的<member>   
375<member>有个属性nameC# 解析器获得关联xml注释对应的成员名字作为这个属性的值。我已经描述过这个值的命名规范,剩下的由<member>所包含的xml由实际的代码文件中的xml注释所组成,前面我已经提及。   
376** **
377
378** 产生帮助文件    
379** 一个文档罗列所有xml注释几乎是不可能的,通常我们使用XSLT去解析XML Html 帮助页面。   
380我新建了一个名叫GiveHelp的工程,它里面包含一个简单的名叫GiveHelpTransforms 的类、几个asp.net页面和转换为html用的XSLT文件。(文章开头有源代码下载链接)。这个Web应用把本地xml文件转换为一个帮助文件。   
381这些asp.net页面把转换后的结果在浏览器中展现,他们使用了GiveHelpTransforms类的接口去转换xmlhtml   
382采用GiveHelpTransforms类有两个理由:首先, 其他工程也可以利用这个代码实现从xmlhtml的转换;其次,在这个示范工程中,我需要一个类来演示这个转换。听起来有些混乱,但是通过我的例子演示,你就会知道该如何做。   
383  
384** 转换    
385**   
386在这一节中,我假设你已经了解了XSLT,因此我只是简单的讲如何把xml文档转换为html   
387我把帮助系统分作三层,最顶层是类列表。这个页面罗列xml文档中所有的类的细节信息,演示看图8。   
388 8  类列表页面    
389  
390
391
392![Figure 8 Type List](http://www.csdn.net/Develop/ArticleImages/19/19051/CSDN_Dev_Image_2003-6-16913552.gif)  
393---  
394  
395类列表页的每一个类的链接都到一个类的所有成员列表的页面。中间层是类成员列表页,这个页面将罗列这个类的所有成员和他们的具体信息。点击一个成员链接,将进入下一层,类成员页。那里罗列类成员的细节、许可、参数、异常、例子和一些其他链接。   
396这一节中使用的XSLTGiveTypeHelp.xsl文件。它在浏览器中把xml转换为罗列所有的类的html。使用一对<dl>来创建类定义表,类的名称被放在<dt>中,详细说明将放在<dd>中,页面左边显示了类名,下面是详细介绍。   
397类名被从文档中萃取。你应该还记得所有的类的名字前缀是"T:" ,于是 xslt应该这样写:   
398
399    
400    
401    <xsl:apply-templates select="./members/member[starts-with
402      (@name,'T:')]"></xsl:apply-templates>
403    
404
405这一行能够在代码块的头找到<doc>元素,以后代码会被找到就像下面:   
406
407    
408    
409    <xsl:template match="member[starts-with(@name,'T:')]">
410    ...
411    </xsl:template>
412    
413
414这个是被<xsl:apply-templates>调用,这两个声明告诉XSLT解析器,仅仅提取满足上述条件的成员。   
415把每个类转换为html页,每个类的名字从name属性中获得,然后每个类的细节描述被提取。图9演示了如何利用<dt>  <dd>处理   
416 9  提取类的名字和细节    
417
418    
419    
420    <dt>
421<a>
422<xsl:attribute name="href">
423             GiveTypeMemberListHelp.aspx?Type=<xsl:value-of select="substring-
424             after(@name,':')"></xsl:value-of>
425</xsl:attribute>
426<b>
427<xsl:value-of select="substring-after(@name,'.')"></xsl:value-of>
428</b>
429</a>
430</dt>
431<dd>
432<xsl:apply-templates select="remarks | summary"></xsl:apply-templates>
433</dd>
434    
435
436<dt>中,一个指向具体类信息的超链接被创建。在<dd>中,<remarks> 或者 <summary>值被提取。下一节我将解释XSLT做了那些处理,图8就是显示了处理结果。   
437  
438**成员 列表**   
439  
440我的例子中,在类列表那一页面,如果一个类被点击,页面将跳转到GiveTypeMemberListHelp.aspx页面,这个页面罗列了该类的所有成员。  10 是对XSLT的一段摘录,更详细的看源代码。   
441 10  GiveTypeMemberListHelp.xsl    
442
443    
444    
445    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
446<xsl:output method="html"></xsl:output>
447<xsl:param name="WhichType"></xsl:param>
448<xsl:template match="doc">
449<xsl:apply-templates select="./assembly/name"></xsl:apply-templates>
450<h2>
451          Members
452        </h2>
453<xsl:apply-templates mode="fields" select="./members/member[starts-
454          with(@name,concat('F:',$WhichType))]"></xsl:apply-templates>
455<xsl:apply-templates mode="properties" select="./members/member[starts- 
456          with(@name,concat('P:',$WhichType))]"></xsl:apply-templates>
457<xsl:apply-templates mode="methods" select="./members/member[starts-
458          with(@name,concat('M:',$WhichType))]"></xsl:apply-templates>
459<xsl:apply-templates mode="events" select="./members/member[starts-
460          with(@name,concat('E:',$WhichType))]"></xsl:apply-templates>
461</xsl:template>
462<xsl:template match="name">
463<table bgcolor="lightskyblue" border="1" cellpadding="10" cellspacing="0" width="100%">
464<tr>
465<td align="center">
466<h1>
467<xsl:text>Assembly: </xsl:text>
468<xsl:value-of select="."></xsl:value-of>
469<br/>
470<xsl:text>Type: </xsl:text>
471<xsl:value-of select="substring-after($WhichType,'.')"></xsl:value-of>
472</h1>
473</td>
474</tr>
475</table>
476</xsl:template>
477<xsl:template match="member" mode="methods">
478<xsl:if test="position() = 1">
479<p></p>
480<h3>Methods</h3>
481</xsl:if>
482<dl>
483<dt>
484<a>
485<xsl:attribute name="href">
486                GiveTypeMemberHelp.aspx?
487                Member=<xsl:value-of select="substring-after(@name,':')"></xsl:value-of>
488</xsl:attribute>
489<b>
490<xsl:value-of select="substring-
491                  after(@name,concat($WhichType,'.'))"></xsl:value-of>
492</b>
493</a>
494</dt>
495<dd>
496<xsl:apply-templates select="remarks | summary"></xsl:apply-templates>
497</dd>
498</dl>
499<p></p>
500</xsl:template>
501    ...
502    
503
504GiveTypeMemberListHelp.aspx 页面被请求的时候,它接受了一个完全的类名GET参数。(比如:/GiveTypeMemberListHelp.aspx?Type=MyNamespace.MyType XML-to-HTML的转换过程中,XSLT把这个值传递给XSLT的参数:WhichType。这个XSLT的参数如下:   
505
506    
507    
508    <xsl:param name="WhichType"></xsl:param>
509    
510
511这个参数用在过滤 <member>。下面是XSLT的具体做法:   
512
513    
514    
515    <xsl:apply-templates mode="fields" select="./members/member[starts-
516      with(@name,concat('F:',$WhichType))]"></xsl:apply-templates>
517<xsl:apply-templates mode="properties" select="./members/member[starts-
518      with(@name,concat('P:',$WhichType))]"></xsl:apply-templates>
519<xsl:apply-templates mode="methods" select="./members/member[starts-
520      with(@name,concat('M:',$WhichType))]"></xsl:apply-templates>
521<xsl:apply-templates mode="events" select="./members/member[starts-
522      with(@name,concat('E:',$WhichType))]"></xsl:apply-templates>
523      
524    
525
526代码很明确,它是调用<member> Fields, Properties, Methods  Events 模板。我使用XPath 来实现这个功能,Xpath的参数就如下:   
527
528    
529    
530    *:Namespace.Type.Member
531    
532
533每个成员的调用模板都很相似,我来解释一下GiveTypeMemberListHelp.xsl 中的模板(看图10) <xsl:if> 这个标记用来判断是不是满足条件,如果满足,将被写到 <dt> 间的部分。 <dd>中间的部分是来自于<summary>  <remarks> 部分数据.   
534  
535** 类成员  **   
536  
537这一节的讨论在 GiveTypeMemberHelp.xsl 文件中。就像类成员列表那一层,类成员层也有一个参数,这个参数就是类成员的全名。 
538    
539    
540    <xsl:param name="WhichMember"></xsl:param>
541    
542
543类可以从类成员全名中获得,于是你可以看到下面的XSLT: 
544    
545    
546    <xsl:param name="WhichType" select="concat
547    (concat( substring-before($WhichMember,'.'),  '.'),
548    substring-before(substring-after($WhichMember,'.'),'.'))"></xsl:param>
549    
550
551XSLT 的开头部分要比上一个要复杂些: 
552    
553    
554    <xsl:template match="doc">
555<xsl:apply-templates select="./assembly/name"></xsl:apply-templates>
556<xsl:apply-templates select="./members/member[contains
557      (@name,$WhichMember)]"></xsl:apply-templates>
558</xsl:template>
559    
560
561第一个<xsl:apply-templates> 是从<name>中提取数据;第二个<xsl:apply-templates> 是提取满足$WhichMember的类成员。下面这一行是从图11中提取的处理成员注释的部分。 
562    
563    
564    <xsl:template match="member[contains(@name,$WhichMember)]">
565    
566
567 11  GiveTypeMemberHelp.xsl 摘录 
568    
569    
570    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
571<xsl:output method="html"></xsl:output>
572<xsl:param name="WhichMember"></xsl:param>
573<xsl:param name="WhichType" select="concat(concat( substring-
574          before($WhichMember,'.'),  '.'), substring-before(substring-
575          after($WhichMember,'.'),'.'))"></xsl:param>
576<xsl:template match="doc">
577<xsl:apply-templates select="./assembly/name"></xsl:apply-templates>
578<xsl:apply-templates select="./members/
579              member[contains(@name,$WhichMember)]"></xsl:apply-templates>
580</xsl:template>
581<xsl:template match="name">
582    ...
583        </xsl:template>
584<xsl:template match="member[contains(@name,$WhichMember)]">
585<h2>
586<xsl:choose>
587<xsl:when test="starts-with(@name,'M:')">
588                        Method
589                    </xsl:when>
590<xsl:when test="starts-with(@name,'F:')">
591                        Field
592                    </xsl:when>
593<xsl:when test="starts-with(@name,'P:')">
594                        Property
595                    </xsl:when>
596<xsl:when test="starts-with(@name,'E:')">
597                        Event
598                    </xsl:when>
599</xsl:choose>
600</h2>
601<table bgcolor="silver" border="1" cellpadding="10" cellspacing="0" width="100%">
602<tr>
603<td>
604<b>
605<xsl:value-of select="substring-
606                              after($WhichMember,concat($WhichType,'.'))"></xsl:value-of>
607</b>
608</td>
609</tr>
610</table>
611<p></p>
612<b>Description:</b>
613<dl>
614<xsl:apply-templates select="remarks"></xsl:apply-templates>
615</dl>
616<dl>
617<xsl:apply-templates select="summary"></xsl:apply-templates>
618</dl>
619<dl>
620<xsl:apply-templates select="value"></xsl:apply-templates>
621</dl>
622<b>Permission:</b>
623<dl>
624<xsl:apply-templates select="permission"></xsl:apply-templates>
625</dl>
626<xsl:if test="not(starts-with(@name,'F:'))">
627<b>Parameters:</b>
628<dl>
629<xsl:apply-templates select="param"></xsl:apply-templates>
630</dl>
631<xsl:if test="not(starts-with(@name,'E:')) and not(starts-
632                  with(@name,'P:'))">
633<b>Returns:</b>
634<dl>
635<xsl:apply-templates select="returns"></xsl:apply-templates>
636</dl>
637</xsl:if>
638<b>Exceptions:</b>
639<dl>
640<xsl:apply-templates select="exception"></xsl:apply-templates>
641</dl>
642</xsl:if>
643<b>Example:</b>
644<dl>
645<xsl:apply-templates select="example"></xsl:apply-templates>
646</dl>
647<b>See Also:</b>
648<dl>
649<xsl:apply-templates select="seealso"></xsl:apply-templates>
650</dl>
651</xsl:template>
652<xsl:template match="summary | remarks | value">
653<dt></dt>
654<dd>
655<xsl:apply-templates></xsl:apply-templates>
656</dd>
657</xsl:template>
658        ...
659        <xsl:template match="*">
660<xsl:copy>
661<xsl:apply-templates></xsl:apply-templates>
662</xsl:copy>
663</xsl:template>
664</xsl:stylesheet>
665    ...
666    
667
668如果你对XSLT比较熟悉,这些都是很简单的。   
669除了插入不同的标记以外,属于这个成员的标记也会被处理。他们是通过<xsl:apply-templates select="summary"></xsl:apply-templates>  <xsl:apply-templates select="param"></xsl:apply-templates>来处理的。你可以在GiveTypeMemberHelp.xsl中找到代码。   
670  
671** 处理标记  **   
672  
673处理完文档的标记之后,开始处理xml注释,这个处理过程在工程中随处可见。   
674在不同的处理中,主标记会分别不同。 在类列表和类成员列表中,它是<summary>  <remarks> 在类成员层,它是primary tag 你可以在XSLT文件中看到: <xsl:apply-templates "primary="" <xsl:template="" match="primary tag" select="primary tag" tag"="" 一旦被执行,就像下面,进程会跳转到对应块。="" 被作为主标记。="">
675    ...
676        <xsl:apply-templates></xsl:apply-templates>
677    ...
678    </xsl:apply-templates></remarks></summary></xsl:template>
679    
680
681  
682primary tag 附近的html标记将通过<xsl:apply-templates></xsl:apply-templates>来创建。 
683
684这些支持的标记在转换中通常被转为html标记。例如<c> 就是做下面的转换。   
685
686    
687    
688    <xsl:template match="c">
689<code>
690<xsl:apply-templates></xsl:apply-templates>
691</code>
692</xsl:template>
693    
694
695Xml注释中的 <c> 对应 html 中的 <code> 更多的对应关系看图12。   
696 12  XML 注释标签与HTML对应关系    
697.NET XML Comment Tag   
698|  My HTML Equivalent   
699  
700---|---  
701<c>   
702|  <code>
703<para>   
704|  <p>
705<paramref>   
706|  <i>
707<see>   
708|  <a>
709<list type="table">   
710|  <table>
711<list type="bullet">   
712|  <ul>
713<list type="number">   
714|  <ol>
715<listheader> (with table)   
716|  <thead>
717<listheader> (with list)   
718|  <li><b>
719<item> (with table and in header)   
720|  <tr>[<th>]   
721  
722<item> (with table)   
723|  <tr>[<td>]   
724  
725<item> (with list)   
726|  <li>
727<term> (with table and in header)   
728|  <th>
729<term> (with table)   
730|  <td>
731<term> (with list)   
732|  Special: adds "-" to end of tag value   
733  
734<description> (with table and in header )   
735|  <th>
736<description> (with table)   
737|  <td>
738<description> (with list)   
739|  None   
740  
741<code>   
742|  <pre>   
743  
744*   
745|  Special: all other nodes are just copied in   
746  
747  
748图13演示了xml(图4)通过XSLT(图11中的XSLT)转换为html   
749 13  转换为html后的GiveHelpDoc.xml    
750
751    
752    
753    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
754
755    <!-- saved from url=(0122)http://localhost/GiveHelp/
756    GiveTypeMemberHelp.aspx?Member=GiveHelp.GiveHelpTransforms.
757      GiveMemberListHTMLHelp(System.String) -->
758    <html>
759      <head>
760          <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
761          <meta content="MSHTML 6.00.2462.0" name="GENERATOR"/>
762          <meta content="C#" name="CODE_LANGUAGE"/>
763          <meta content="JavaScript (ECMAScript)" name="vs_defaultClientScript"/>
764          <meta content="http://schemas.microsoft.com/intellisense/ie5" name="vs_targetSchema"/>
765      </head>
766      <body>
767          <table bgcolor="lightskyblue" border="1" cellpadding="10" cellspacing="0" width="100%">
768              <tbody>
769                  <tr>
770                    <td align="middle">
771                          <h1>
772                              Assembly: GiveHelp
773                              <br/>
774                              Type: GiveHelpTransforms
775                          </h1>
776                      </td>
777                  </tr>
778              </tbody>
779          </table>
780          <h2>
781              Method
782          </h2>
783          <table bgcolor="silver" border="1" cellpadding="10" cellspacing="0" width="100%">
784              <tbody>
785                  <tr>
786                      <td>
787                          <b>GiveMemberListHTMLHelp(System.String)</b>
788                      </td>
789                  </tr>
790              </tbody>
791          </table>
792          <p>
793          </p>
794          <b>Description:</b>
795          <dl>
796          </dl>
797          <dl>
798              <dt>
799                  <dd>
800                      Calling this function will take the XML in 
801                        <a href="http://localhost/GiveHelp/   
802                          GiveTypeMemberHelp.aspx?Type=GiveHelp.
803                          GiveHelpTransforms&amp;Member=GiveHelp.
804                          GiveHelpTransforms.SourceXML"> SourceXML</a>
805                          and translate it to a list of 
806                          Members in the specified type.
807                  </dd>
808              </dt>
809          </dl>
810          <dl>
811          </dl>
812          <b>Permission:</b>
813          <dl>
814              <dt>
815                  <dd>
816                      <dl>
817                          <dt><a href="http://localhost/GiveHelp/
818                               GiveTypeHelp.aspx?Type=."></a>
819                              <dd>
820                                  public
821                              </dd>
822                          </dt>
823                      </dl>
824                  </dd>
825              </dt>
826          </dl>
827          <b>Parameters:</b>
828          <dl>
829              <dt>
830                  <dd>
831                      <dl>
832                          <dt><i>strType</i>
833                              <dd>
834                                  The fully qualified name of the type that 
835                                  the member is in.
836                              </dd>
837                      </dt></dl>
838                  </dd>
839          </dt></dl>
840          <b>Returns:</b>
841          <dl>
842              <dt>
843                  <dd>
844                      The HTML that lists the types that are in the 
845                      XML documentation.
846                  </dd>
847              </dt>
848          </dl>
849          <b>Exceptions:</b>
850          <dl>
851          </dl>
852          <b>Example:</b>
853          <dl>
854              <dt>
855                  <dd>
856                      <pre> // create the class that does translations
857              
858              GiveHelpTransforms ght = new GiveHelpTransforms();
859              // have it load our XML into the SourceXML property
860              ght.LoadXMLFromFile(
861                      "E:\\Inetpub\\wwwroot\\GiveHelp\\GiveHelpDoc.xml");
862                
863              // do the translation and then write out the string
864              Response.Write( 
865                  ght.GiveMemberListHTMLHelp(Request.QueryString.Get("Type")));
866              </pre>
867            </dd>
868          </dt>
869          </dl>
870          <b>See Also:</b>
871          <dl>
872              <dt>
873                  <dd>
874                      <a href="http://localhost/GiveHelp/     
875                      GiveTypeMemberHelp.aspx?Type=GiveHelp.
876                      GiveHelpTransforms&amp;Member=GiveHelp.GiveHelpTransforms.
877                      GiveTypeListHTMLHelp">
878                      GiveHelpTransforms.GiveTypeListHTMLHelp</a> <dt></dt>
879                      <dd>
880                          <a href="http://localhost/GiveHelp/    
881                          GiveTypeMemberHelp.aspx?Type=GiveHelp.
882                          GiveHelpTransforms&amp;Member=GiveHelp.
883                          GiveHelpTransforms.GiveMemberHTMLHelp(System.String)">
884                          GiveHelpTransforms.GiveMemberHTMLHelp(System.String)
885                          </a>
886                      </dd>
887                  </dd>
888              </dt>
889          </dl>
890      </body>
891    </html>
892    
893
894** 总结  **
895
896** ** 虽然很早就已经有工具可以从源代码中提取注释,但是他们都没有被广泛的应用。原因大多都是使用复杂或者没有跟主流开发工具集成在一起。 C#中基于XML的注释,把语言跟工具很好的集成到一起,从而使使用xml注释l变得相当吸引人。</pre></code></description></td></description></th></description></term></td></term></th></term></li></item></td></tr></item></th></tr></item></b></li></listheader></thead></listheader></ol></list></ul></list></table></list></a></see></i></paramref></p></para></code></c></code></c></c></xsl:apply-templates></name></xsl:apply-templates></remarks></summary></dd></dt></xsl:if></member></member></xsl:stylesheet></summary></remarks></dd></dt></dd></dt></xsl:apply-templates></doc></dd></dt></dl></member></member></member></members></name></name></name></assembly></assembly></doc></see></include></seealso></permission></exception></summary></remarks></value></exception></see></paramref></p></para></item></listheader></description></term></description></term></item></listheader></list></list></list></c></code></c></code></example></code></c></value></see></paramref></para></description></term></item></listheader></list></code></c></include></include></include></seealso></returns></permission></permission></exception></exception></exception></exception></code></code></example></remarks></summary></summary></remarks></remarks></summary></summary></remarks></remarks></include></seealso></returns></permission></exception></example></summary></remarks>
Published At
Categories with Web编程
Tagged with
comments powered by Disqus