电脑爱好者,提供IT资讯信息及各类编程知识文章介绍,欢迎大家来本站学习电脑知识。 最近更新 | 联系我们 RSS订阅本站最新文章
电脑爱好者
站内搜索: 
当前位置:首页>> C#>>展现C#:第九章 配置和调度:

展现C#:第九章 配置和调度

来源:网络 | 2007-5-3 | (有1599人读过)

    在上一章,你学到如何创建一个通用语言运行时(CLR)组件,且如何在一个简单的测试应用程序中使用它。虽然CLR组件就要准备装载了,但你还是应该思考以下技术之一: 
  。条件编译 
  。文档注释 
  。代码版本化 

9.1   条件编译   
    没有代码的条件编译功能,我就不能继续工作。条件编译允许执行或包括基于某些条件的代码;例如,生成应用程序 
的一个查错(DEBUG)版本、演示(DEMO)版本或零售(RELEASE)版本。可能被包括或被执行的代码的例子为许可证代 
码、 屏幕保护或你出示的任何程序。 
    在C#中,有两种进行条件编译的方法: 
    。预处理用法 
    。条件属性 
9.1.1   预处理用法 
    在C++中,在编译器开始编译代码之前,预处理步骤是分开的。在C#中,预处理被编译器自己模拟—— 没有分离的预 
处理。它只不过是条件编译。 
    尽管C#编译器不支持宏,但它具有必需的功能,依据符号定义的条件,排除和包括代码。以下小节介绍了在C#中受支 
持的各种标志,它们与在C++中看到的相似。 
    。定义符号 
    。依据符号排除代码 
    。引起错误和警告 
9.1.1.1  定义符号 
    你不能使用随C#编译器一起的预处理创建“define 标志:符号:定义 ”宏,但是,你仍可以定义符号。根据某些符号 
是否被定义,可以排除或包括代码。 
    第一种定义符号的办法是在C#源文件中使用 #define标志: 
    #define DEBUG 
    这样定义了符号DEBUG,且范围在它所定义的文件内。请注意,必须要先定义符号才能使用其它语句。例如,以下代码 
段是不正确的: 

    using System; 
    #define DEBUG 

    编译器将标记上述代码为错误。你也可以使用编译器定义符号(用于所有的文件): 
    csc /define:DEBUG mysymbols.cs 
    如果你想用编译器定义多种符号,只需用分号隔开它们: 
    csc /define:RELEASE;DEMOVERSION mysymbols.cs 
    在C#源文件中,对这两种符号的定义分为两行 #define 标志。 
    有时,你可能想要取消源文件中(例如,较大项目的源文件)的某种符号。可以用 #undef 标志取消定义: 
    #undef DEBUG 
    #define的“定义标志:符号: 定义”规则同样适用于#undef:  它的范围在自己定义的文件之内,要放在任何语句如 
using语句之前。 
    这就是全部有关用C#预处理定义符号和取消定义符号所要了解的知识。以下小节说明如何使用符号有条件地编译代 
码。 

9.1.1.2 依据符号包括和排除代码 
    最重要的“if标志:符号:包括代码”方式的目的为,依据符号是否被定义,有条件地包括和排除代码。**9.1  包含 
了已出现过的源码,但这次它依据符号被有条件地编译。 

    ** 9.1  利用 #if 标志有条件地包括代码 

1: using System; 
2:  
3: public class SquareSample 
4: { 
5: public void CalcSquare(int nSideLength, out int nSquared) 
6: { 
7:  nSquared = nSideLength * nSideLength; 
8: } 
9:  
10: public int CalcSquare(int nSideLength) 
11: { 
12:  return nSideLength*nSideLength;13: } 
14: } 
15:  
16: class SquareApp 
17: { 
18: public static void Main() 
19: { 
20:  SquareSample sq = new SquareSample(); 
21:   
22:  int nSquared = 0; 
23:  
24: #if CALC_W_OUT_PARAM 
25:  sq.CalcSquare(20, out nSquared); 
26: #else  
27:  nSquared = sq.CalcSquare(15); 
28: #endif 
29:  Console.WriteLine(nSquared.ToString()); 
30: } 
31: } 

    注意,在这个源文件中没有定义符号。当编译应用程序时,定义(或取消定义)符号: 
    csc /define:CALC_W_OUT_PARAM square.cs 
    根据“ if标志:符号:包括代码”的符号定义,不同的 CalcSquare 被调用了。用来对符号求值的模拟预处理标志为 
#if、 #else和 #endif。它们产生的效果就象C#相应的if 语句那样。你也可以使用逻辑“与”(&&)、逻辑“或” 
(¦¦)以及“否”(!)。它们的例子显示在**9.2 中。 

    ** 9.2  使用#elif 在#if标志中创建多个分支 

1: // #define DEBUG 
2: #define RELEASE 
3: #define DEMOVERSION 
4:  
5: #if DEBUG 
6: #undef DEMOVERSION 
7: #endif 
8:  
9: using System; 
10:  
11: class Demo 
12: { 
13: public static void Main() 
14: { 
15: #if DEBUG 
16:  Console.WriteLine("Debug version"); 
17: #elif RELEASE && !DEMOVERSION 
18:  Console.WriteLine("Full release version"); 
19: #else 
20:  Console.WriteLine("Demo version"); 
21: #endif 
22: } 
23: } 

    在这个“if标志:符号:包含代码”例子中,所有的符号都在C#源文件中被定义。注意第6行#undef语句增加的那部分。 
由于不编译DEBUG代码的DEMO版本(任意选择),我确信它不会被某些人无意中定义了,而且总当DEBUG被定义时,就取消 
DEMO版本的定义。 
    接着在第15~21行,预处理符号被用来包括各种代码。注意#elif标志的用法,它允许你把多个分支加到#if 标志。该 
代码运用逻辑操作符“&&”和非操作符“!”。也可能用到逻辑操作符“¦¦”,以及等于和不等于操作 
符。 

9.1.1.3 引起错误并警告 
    另一种可能的“警告  标志错误  标志”预处理标志的使用,是依据某些符号(或根本不依据,如果你这样决定)引 
起错误或警告。各自的标志分别为 #warning和#error,而**9.3 演示了如何在你的代码中使用它们。 
    ** 9.3   使用预处理标志创建编译警告和错误 

1: #define DEBUG 
2: #define RELEASE 
3: #define DEMOVERSION 
4:  
5: #if DEMOVERSION && !DEBUG 
6: #warning You are building a demo version 
7: #endif 
8:  
9: #if DEBUG && DEMOVERSION 
10: #error You cannot build a debug demo version 
11: #endif 
12:  
13: using System; 
14:  
15: class Demo 
16: { 
17: public static void Main() 
18: { 
19:  Console.WriteLine("Demo application"); 
20: } 
21: } 

    在这个例子中,当你生成一个不是DEBUG版本的DEMO版本时,就发出了一个编译警告(第5行~第7行)。当你企图生成 
一个DEBUG DEMO版本时,就引起了一个错误,它阻止了可执行文件的生成。对比起前面只是取消定义令人讨厌的符号的例 
子,这些代码告诉你,“警告  标志错误 标志”企图要做的工作被认为是错误的。这肯定是更好的处理办法。 
9.1.1.4  条件属性 
    C++的预处理也许最经常被用来定义宏,宏可以解决一种程序生成时的函数调用,而却不能解决另一种程序生成时的任何问题。这些例子包括 ASSERT和TRACE 宏,当定义了DEBUG符号时,它们对函数调用求值,当生成一个RELEASE版本时,求 
值没有任何结果。 

    当了解到宏不被支持时,你也许会猜测,条件功能已经消亡了。幸亏我可以报道,不存在这种情况。你可以利用条件 
属性,依据某些已定义符号来包括方法。: 

     [conditional("DEBUG")] 
     public void SomeMethod() { } 

    仅当符号DEBUG被定义时,这个方法被加到可执行文件。并且调用它,就象 
    SomeMethod(); 

    当该方法不被包括时,它也被编译器声明。功能基本上和使用C++条件宏相同。 
    在例子开始之前,我想指出,条件方法必须具有void的返回类型,不允许其它返回类型。然而,你可以传递你想使用 
的任何参数。 
    在**9.4 中的例子演示了如何使用条件属性重新生成具有C++的TRACE宏一样的功能。为简单起见,结果直接输出到 
屏幕。你也可以根据需要把它定向到任何地方,包括一个文件。 

    ** 9.4  使用条件属性实现方法 

1: #define DEBUG 
2:  
3: using System; 
4:  
5: class Info 
6: { 
7: [conditional("DEBUG")] 
8: public static void Trace(string strMessage) 
9: { 
10:  Console.WriteLine(strMessage); 
11: } 
12:  
13: [conditional("DEBUG")] 
14: public static void TraceX(string strFormat,params object[] list) 
15: { 
16:  Console.WriteLine(strFormat, list); 
17: } 
18: } 
19:  
20: class TestConditional 
21: { 
22: public static void Main() 
23: { 
24:  Info.Trace("Cool!"); 
25:  Info.TraceX("{0} {1} {2}","C", "U", 2001); 
26: } 
27: } 

    在Info类中,有两个静态方法,它们根据DEBUG符号被有条件地编译:Trace,接收一个参数,而TraceX则接收n个参 
数。Trace的实现直接了当。然而,TraceX实现了一个你从没有见过的关键字:params。 
    params 关键字允许你指定一个方法参数,它实际上接收了任意数目的参数。其类似C/C++的省略参数。注意,它必须 
是方法调用的最后一个参数,而且在参数列表中,你只能使用它一次。毕竟,它们的局限性极其明显。 
    使用params 关键字的意图就是要拥有一个Trace方法,该方法接收一个格式字符串以及无数个置换对象。幸好,还有 
一个支持格式字符串和对象数组的 WriteLine方法(第16行)。 
    这个小程序产生的哪一个输出完全取决于DEBUG是否被定义。当DEBUG符号被定义时,方法都被编译和执行。如果DEBUG 
不被定义,对Trace和TraceX的调用也随之消失。 
    条件方法是给应用程序和组件增加条件功能的一个真正强大的手段。用一些技巧,你就可以根据由逻辑“或” 
(¦¦)以及逻辑“与”(&&)连接起来的多个符号,生成条件方法。然而,对于这些方案,我想给你推荐C# 
文档。 
   
9.2  在XML中的文档注释 
    很多程序员根本不喜欢的一项任务就是写作,包括写注释和写文档。然而,有了C#,你就找到改变老习惯的好理由: 
你可以用代码的注释自动生成文档。 
    由编译器生成的输出结果是完美的XML。它可以作为组件文档的输入被使用,以及作为显示帮助并揭示组件内部细节的 
工具。例如, Visual Studio 7 就是这样一种工具。 
    这一节专门为你说明如何最好地运用C#的文档功能。该例子涉及的范围很广,所以你不能有这样的借口,说它过于复杂,以至很难领会如何加入文档注释。文档是软件极其重要的一部分,特别是要被其他开发者使用的组件的文档。 
    在以下小节中,文档注解用来说明RequestWebPage 类。我已分别在以下几小节中做出解释: 
    。描述一个成员 
    。添加备注和列表 
    。提供例子 
    。描述参数 
    。描述属性 
    。编译文档 


9.2.1  描述一个成员 
    第一步,为一个成员添加一个简单的描述。你可以用 <summary> 标签这样做: 
/// <summary>This is .... </summary> 


    每一个文档注释起始于由三个反斜杠组成的符号“///”。你可以把文档注释放在想要描述的成员之前: 

/// <summary>Class to tear a Webpage from a Webserver</summary> 

public class RequestWebPage 

    使用<para>和 </para>标签,为描述添加段落。用<see>标签引用其它已有了注释的成员。 
/// <para>Included in the <see cref="RequestWebPage"/> class</para> 

    增加一个链接到RequestWebPage类的描述。注意,用于标签的语法是XML语法,这意味着标签大写化的问题,而且标签 
必须正确地嵌套。 
    当为一个成员添加文档时,另一个有趣的标签是<seealso> 。它允许你描述可能使读者非常感兴趣的其它话题。 

/// <seealso cref="System.Net"/> 

    前面的例子告诉读者,他可能也想查阅System.Net 名字空间的文档。你一定要给超出当前范围的项目规定一个完全资 
格名。 
    作为许诺,**9.5 包含 RequestWebPage类中正在工作的文档的所有例子。看一下如何使用标签以及嵌套如何为组件 
产生文档。 

    ** 9.5  利用 <summary>, <see>, <para>, and <seealso> 标签描述一个成员 

1: using System; 
2: using System.Net; 
3: using System.IO; 
4: using System.Text; 
5:  
6: /// <summary>Class to tear a Webpage from a Webserver</summary> 
7: public class RequestWebPage 
8: { 
9: private const int BUFFER_SIZE = 128; 
10:  
11: /// <summary>m_strURL stores the URL of the Webpage</summary> 
12: private string m_strURL; 
13:  
14: /// <summary>RequestWebPage() is the constructor for the class  
15: /// <see cref="RequestWebPage"/> when called without arguments.</summary> 
16: public RequestWebPage() 
17: { 
18: } 
19:  
20: /// <summary>RequestWebPage(string strURL) is the constructor for the class 
21: /// <see cref="RequestWebPage"/> when called with an URL as parameter.</summary> 
22: public RequestWebPage(string strURL) 
23: { 
24:  m_strURL = strURL; 
25: } 
26:  
27: public string URL 
28: { 
29:  get { return m_strURL; } 
30:  set { m_strURL = value; } 
31: } 
32:  
33: /// <summary>The GetContent(out string strContent) method: 
34: /// <para>Included in the <see cref="RequestWebPage"/> class</para> 
35: /// <para>Uses variable <see cref="m_strURL"/></para> 
36: /// <para>Used to retrieve the content of a Webpage. The URL 
37: ///  of the Webpage (including http://) must already be 
38: ///  stored in the private variable m_strURL.  
39: ///  To do so, call the constructor of the RequestWebPage  
40: ///  class, or set its property <see cref="URL"/> to the URL string.</para> 
41: /// </summary> 
42: /// <seealso cref="System.Net"/> 
43: /// <seealso cref="System.Net.WebResponse"/> 
44: /// <seealso cref="System.Net.WebRequest"/> 
45: /// <seealso cref="System.Net.WebRequestFactory"/> 
46: /// <seealso cref="System.IO.Stream"/>  
47: /// <seealso cref="System.Text.StringBuilder"/> 
48: /// <seealso cref="System.ArgumentException"/> 
49:  
50: public bool GetContent(out string strContent) 
51: { 
52:  strContent = ""; 
53:  // ... 
54:  return true; 
55: } 
56: } 

9.2.2  添加备注和列表 
     <remarks> 标签是规定大量文档的地方。与之相比, <summary>只仅仅规定了成员的简短描述。 
     你不限于只提供段落文本(使用<para>标签)。例如,你可以在备注部分包含bulleted(和有限偶数)列表 
(list): 

///  <list type="bullet"> 
///  <item>Constructor  
///   <see cref="RequestWebPage()"/> or 
///   <see cref="RequestWebPage(string)"/> 
///  </item> 
///  </list> 

    这个list有一项(item),且该item引用了两个不同的构造函数描述。你可以根据需要,任意往list item中添加内 
容。 
    另一个在备注部分很好用的标签是<paramref>。例如,你可以用<paramref>来引用和描述传递给构造函数的参数: 

/// <remarks>Stores the URL from the parameter /// <paramref name="strURL"/> in  
/// the private variable <see cref="m_strURL"/>.</remarks> 
public RequestWebPage(string strURL) 

    在**9.6中,你可以看到所有的这些以及前面的标签正在起作用。 

    **9.6 为文档添加一个备注和bullet list 

1: using System; 
2: using System.Net; 
3: using System.IO; 
4: using System.Text; 
5:  
6: /// <summary>Class to tear a Webpage from a Webserver</summary> 
7: /// <remarks>The class RequestWebPage provides: 
8: /// <para>Methods: 
9: ///  <list type="bullet"> 
10: ///  <item>Constructor  
11: ///   <see cref="RequestWebPage()"/> or 
12: ///   <see cref="RequestWebPage(string)"/> 
13: ///  </item> 
14: ///  </list> 
15: /// </para> 
16: /// <para>Properties: 
17: ///  <list type="bullet"> 
18: ///  <item> 
19: ///   <see cref="URL"/> 
20: ///  </item> 
21: ///  </list> 
22: /// </para> 
23: /// </remarks> 
24: public class RequestWebPage 
25: { 
26: private const int BUFFER_SIZE = 128; 
27:  
28: /// <summary>m_strURL stores the URL of the Webpage</summary> 
29: private string m_strURL; 
30:  
31: /// <summary>RequestWebPage() is the constructor for the class 32: /// <see cref="RequestWebPage"/> when called without arguments.</summary> 
33: public RequestWebPage() 
34: { 
35: } 
36:  
37: /// <summary>RequestWebPage(string strURL) is the constructor for the class 
38: /// <see cref="RequestWebPage"/> when called with an URL as parameter.</summary> 
39: /// <remarks>Stores the URL from the parameter <paramref name="strURL"/> in 
40: /// the private variable <see cref="m_strURL"/>.</remarks> 
41: public RequestWebPage(string strURL) 
42: { 
43:  m_strURL = strURL; 
44: } 
45:  
46: /// <remarks>Sets the value of <see cref="m_strURL"/>. 
47: ///  Returns the value of <see cref="m_strURL"/>.</remarks> 
48: public string URL 
49: { 
50:  get { return m_strURL; } 
51:  set { m_strURL = value; } 
52: } 
53:  
54: /// <summary>The GetContent(out string strContent) method: 
55: /// <para>Included in the <see cref="RequestWebPage"/> class</para> 
56: /// <para>Uses variable <see cref="m_strURL"/></para> 
57: /// <para>Used to retrieve the content of a Webpage. The URL 
58: ///  of the Webpage (including http://) must already be  
59: ///  stored in the private variable m_strURL.  
60: ///  To do so, call the constructor of the RequestWebPage  
61: ///  class, or set its property <see cref="URL"/> to the URL string.</para> 
62: /// </summary> 
63: /// <remarks>Retrieves the content of the Webpage specified in  
64: /// the property<see cref="URL"/> and hands it over to the out  
65: /// parameter <paramref name="strContent"/>. 
66: /// The method is implemented using: 
67: /// <list> 
68: ///  <item>The <see cref="System.Net.WebRequestFactory.Create"/>method.</item> 
69: ///  <item>The <see cref="System.Net.WebRequest.GetResponse"/> method.</item> 
70: ///  <item>The <see cref="System.Net.WebResponse.GetResponseStream"/>method</item> 
71: ///  <item>The <see cref="System.IO.Stream.Read"/> method</item> 
72: ///  <item>The <see cref="System.Text.StringBuilder.Append"/> method</item> 
73: ///  <item>The <see cref="System.Text.Encoding.ASCII"/> property together with its 
74: ///  <see cref="System.Text.Encoding.ASCII.GetString"/> method</item> 
75: ///  <item>The <see cref="System.Object.ToString"/> method for the  
76: ///  <see cref="System.IO.Stream"/> object.</item> 
77: /// </list> 
78: /// </remarks> 
79: /// <seealso cref="System.Net"/> 
80: public bool GetContent(out string strContent) 
81: { 
82:  strContent = ""; 
83:  // ... 
84:  return true; 
85: } 
86: } 

9.2.3 提供例子 
    要想说明一个对象和方法的用法,最好的办法是提供优秀源代码的例子。因此,不要诧异文档注释也有用于声明例子 
的标签: <example> and <code>。 <example>标签包含了包括描述和代码的整个例子,而 <code> 标签仅包含了例子的代码(令人惊讶)。 
    **9.7 说明如何实现代码例子。包括的例子用于两个构造函数。你必须给GetContent方法提供例子。 

    **.7  利用例子解释概念 

1: using System; 
2: using System.Net; 
3: using System.IO; 
4: using System.Text; 
5:  
6: /// <summary>Class to tear a Webpage from a Webserver</summary> 
7: /// <remarks> ... </remarks> 
8: public class RequestWebPage 
9: { 
10: private const int BUFFER_SIZE = 12  
C#热门文章排行
网站赞助商
购买此位置

 

关于我们 | 网站地图 | 文档一览 | 友情链接| 联系我们

Copyright © 2003-2024 电脑爱好者 版权所有 备案号:鲁ICP备09059398号