欢迎光临
我们一直在努力

<展现C#> 第九章 配置和调度(rainbow 翻译)

建站超值云服务器,限时71元/月

第九章 配置和调度

在上一章,你学到如何创建一个通用语言运行时(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 语句那样。你也可以使用逻辑“与”(&&)、逻辑“或”

(&brvbar;&brvbar;)以及“否”(!)。它们的例子显示在清单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 标志。该

代码运用逻辑操作符“&&”和非操作符“!”。也可能用到逻辑操作符“&brvbar;&brvbar;”,以及等于和不等于操作

符。

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的调用也随之消失。

条件方法是给应用程序和组件增加条件功能的一个真正强大的手段。用一些技巧,你就可以根据由逻辑“或”

(&brvbar;&brvbar;)以及逻辑“与”(&&)连接起来的多个符号,生成条件方法。然而,对于这些方案,我想给你推荐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

赞(0)
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com 特别注意:本站所有转载文章言论不代表本站观点! 本站所提供的图片等素材,版权归原作者所有,如需使用,请与原作者联系。未经允许不得转载:IDC资讯中心 » <展现C#> 第九章 配置和调度(rainbow 翻译)
分享到: 更多 (0)