【重走C#之路】之预处理器指令

          虽然编译器没有单独的预处理器,但在处理以下指令时如同存在一个单独的预处理器,这些指令用于辅助条件编译,不能使用这些指令创建宏,而且这些指令必须是行上的唯一指令。

1.#if

          #if可以开始条件指令,测试一个或多个符号以查看它们是否计算为true。如果为true,则编译器将计算位于#if与最近的#endif指令之间的所有代码。如:

1          #define  Debug
2          //...
3          #if  Debug
4             Response.Write( " 测试代码 " );
5          #endif

           可以使用==、!=、&&、||来计算多个符号,还可以用括号和运算符分组。

          使用#if以及#else、#elif、#endif、#define和#undef指令,可以包括或排除基于由一个或多个符号组成的条件的代码,这在编译调试版本的代码或编译特定配置时最为有用。以#if指令开始的条件指令必须用#endif指令显式终止。使用#define可以定义一个符号,通过将该符号用作传递给#if指令的表达式,使表达式计算为true。可以用#undef来取消定义符号。用#define创建的符号的范围是在其中定义该符号的文件。

代码
 1          #define  DEBUG
 2          #define  VC_V7 // 定义预处理器符号
 3          using  System;
 4          public   class  MyClass 
 5         {
 6             static   void  Main() 
 7            {
 8          #if  (DEBUG && !VC_V7)
 9                 Console.WriteLine( " DEBUG is defined " );
10          #elif  (!DEBUG && VC_V7)
11                 Console.WriteLine( " VC_V7 is defined " );
12          #elif  (DEBUG && VC_V7)
13                Console.WriteLine( " DEBUG and VC_V7 are defined " ); // 执行此分支
14          #else
15                Console.WriteLine( " DEBUG and VC_V7 are not defined " );
16          #endif
17            }
18         }

 2.#else

        #else用于创建复合条件指令,因此,如果前面的#if或#elif指令中的任何表达式都不为true,则编译器将计算#else与后面的#endif之间的所有代码。#endif必须是#else之后的下一条预处理指令。有关#else的用法,参见#if。

3.#elif

        #elif用于创建复合条件指令,如果前面的#if和前面的任何#elif指令表达式的计算结果都不为true,则将计算#elif表达式。如果#elif表达式计算为true,编译器将计算位于#elif和下个条件指令间的所有代码。有关#elif的用法,参见#if。

4.#endif

        #endif指定以#if指令开头的条件指令的结尾。有关用法参见#if。

5.#define

        #define用于定义一个符号,并通过将该符号用作表达式传递给#if指令,使该表达式计算结果为true。例如:

        #define  Debug

         符号可用于指定编译的条件。可以使用#if或#elif来测试符号。还可以使用conditional属性执行条件编译。可以定义符号,但是无法对符号赋值。#define指令必须在使用任何不是指令的指令之前出现在文件中。也可以用/define编译器选项来定义符号。可以用#undef来取消定义符号。用/define或#define定义的符号与具有同一名称的变量不冲突。有关#define的用法,参见#if。

6.#undef

        #undef可以取消符号的定义,以便通过将该符号用作#if中的表达式,使表达式的计算结果为false。

代码
 1           #undef  DEBUG
 2           using  System;
 3           class  MyClass 
 4          {
 5               static   void  Main() 
 6              {
 7           #if  DEBUG
 8                  Console.WriteLine( " DEBUG is defined " );
 9           #else
10                  Console.WriteLine( " DEBUG is not defined " ); // DEBUG不存在,故执行此分支
11           #endif
12              }
13          }

  7.#warning

        #warning用于在代码的特定位置生成一级警告。#warning通常用在条件指令中。也可以用#error生成用于定义的错误。

代码
 1           #define  DEBUG
 2           class  MainClass 
 3          {
 4               static   void  Main() 
 5              {
 6           #if  DEBUG
 7           #warning  DEBUG is defined // 生成一条警告
 8           #endif
 9              }
10          }
11 

 8.#error

        #error用于在代码中的特定位置生成错误,通常用在条件指令中。也可以用#warning生成用户定义的警告。

代码
 1           #define  DEBUG
 2           class  MainClass 
 3          {
 4               static   void  Main() 
 5              {
 6           #if  DEBUG
 7           #error  DEBUG is defined // 编译时生成一条错误,编译器退出
 8           #endif
 9              }
10          }

 9.#line

        #line用于修改编译器的行号以及错误和警告的文件名输出。

代码
 1           class  MainClass
 2          {
 3               static   void  Main() 
 4              {
 5           #line  200
 6                   int  i;     //  将该行行号修改为200
 7           #line  default
 8                   char  c;    //  将该行行号回复默认
 9              }
10          }
11 

         #line指令可能由生成过程中的自动中间步骤使用。例如,如果行从原始的源代码文件中移除,仍希望编译器基于文件中的原始行号生成输出,则可以移除行,然后用#line模拟原始行号。#line hidden指令对调试器隐藏若干连续的行,这样当开发人员在逐句通过代码时,将会跳过#line hidden和下一个#line指令之间的所有行。#line hidden指令不回影响错误报告中的文件名或行号。即,如果在隐藏块中遇到错误,编译器将报告当前文件和错误行号。

代码
 1           using  System;
 2           class  MainClass 
 3          {
 4               static   void  Main() 
 5              {
 6                  Console.WriteLine( " Normal line #1. " ); 
 7           #line  hidden
 8                  Console.WriteLine( " Hidden line. " ); // 调试器将忽略该隐藏行
 9           #line  default
10                  Console.WriteLine( " Normal line #2. " );
11              }
12          }

 10.#region

        #region可以在使用vs代码编辑器的大纲显示功能时指定可展开或折叠的代码块。

        #region块必须以#endregion指令终止。#region块不能与#if块重叠,但可以将#region块嵌套在#if块内,或将#if块嵌套在#region块内。

11.#endregion

        #endregion标记#region块的结尾。

12.#pragma

        #pragma用于给编辑器提供特殊的指令,说明如何编译包含杂注的文件。

         #pragma  pragma-name pragma-arguments

         pargma-name:可识别杂注的名称;pragma-arguments:杂注特定的参数。

13.#pragma warning

        #pragma warning 可用于启用或禁用某些警告。

         #pragma  warning disable warning-list
        
#pragma  warning restore warning-list

         warning-list:警告编号的逗号分隔列表,只输入数字,不包括前缀“CS”。当没有指定警告编号时,disable禁用所有警告,而restore启用所有警告。

代码
 1           #pragma  warning disable 414, 3021
 2              [CLSCompliant( false )]
 3               public   class  C
 4              {
 5                   int  i  =   1 ;
 6                   static   void  Main()
 7                  {
 8                  }
 9              }
10           #pragma  warning restore 3021
11              [CLSCompliant( false )]   //  CS3021
12               public   class  D
13              {
14                   int  i  =   1 ;
15                   public   static   void  F()
16                  {
17                  }
18              }
19 

14.#pragma checksum

         #pragma checksum用于生成源文件的校验和,以帮助调试ASP.NET页。

         #pragma  checksum "filename" "{guid}" "checksum bytes"

          filename:要求监视更改或更新的文件名称。{guid}:文件的全局唯一标识符(GUID)。checksum_bytes:十六进制的字符串,表示校验和的字节。必须是偶数位的十六进制数。基数位的数字会导致编译时警告,从而使指令被忽略。Visual Studio 调试器使用校验和来确保找到的总是正确的源。编译器计算源文件的校验和,然后将输出发出到程序数据库 (PDB) 文件。最后,调试器使用 PDB 来比较它为源文件计算的校验和。此解决方案不适用于 ASP.NET 项目,因为算出的是生成的源文件而不是 .aspx 文件的校验和。为解决此问题,#pragma checksum 为 ASP.NET 页提供了校验和支持。在 Visual C# 中创建 ASP.NET 项目时,生成的源文件包含 .aspx 文件(从该文件生成源文件)的校验和。然后,编译器将此信息写入 PDB 文件。如果编译器在该文件中没有遇到 #pragma checksum 指令,它将计算校验和,然后将算出的值写入 PDB 文件。

 

你可能感兴趣的:(C#)