第九章 配置和调度
在上一章,你学到如何创建一个通用语言运行时(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 |