A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 康子龙 中级黑马   /  2012-10-20 04:01  /  1931 人查看  /  2 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

在C#中有许多名为“预处理器指令”的命令。这些命令从来不会转化为可执行代码中的命令,但会影响编译过程的各个方面。例如,使用预处理器指令可以禁止编译器编译代码的某一部分。但需要注意,C#并没有一个像C++那样的独立预处理器,所谓的预处理器指令实际上是由编译器处理的。
以下简要介绍预处理器指令的功能:
1.#define和#undef
#define的用法如下所示:
#define DEBUG
它告诉编译器存在给定名称的符号,在本例中是DEBUG。这个符号并不是实际代码的一部分,而只在编译器编译代码时存在。在C#代码中没有任何意义。

#undef正好相反——它删除符号的定义:
#undef DEBUG
如果符号不存在,#undef没有任何作用。同样,如果符号已经存在,则#define不起作用。

必须把#define和#undef命令放在C#源文件的开头位置,在声明要编译的任何对象的代码之前。
#define的作用体现在与其他预处理器指令(如#if)结合使用时,它的功能就非常强大了。
2.#if,#elif,#else和#endif
这些指令告诉编译器是否要编译某个代码块。下面例子:
void DoSomething(int x)
{
//do something
#if DEBUG
Console.WriteLine("x="+x);
#endif
}
这段代码会像不同代码一样正常编译,但Console.WriteLine命令包含在了#if子句内。这段代码只有在前面的#define命令定义了符号DEBUG后才执行。当编译器遇到#if语句后,将先检查相关的符号是否存在,如果符号存在,就编译#if子句中的代码。否则,忽略代码,直到遇到匹配的#endif指令为止。一般是在调试时定义DEBUG符号的,把相关的调试代码放到#if子句中。在完成调试后注释掉#define语句。这样调试代码就会消失,编译后的文件也会变小。

#elif(=else if)和#else指令可以用在#if块中,也可以嵌套#if块:
#define ENTERPRISE
#define W2K

#if ENTERPRISE
// do something
#if W2K
  //一些只和ENTERPRISE相关的代码
  //在W2K上运行的代码
#endif
#elif PROFESSIONAL
//do something else
#else
//something
#endif
#if和#elif还支持一组逻辑运算符“!”,"==","!=","||"如果符号存在就被认为是true否则为false,例如:

#if W2K && (ENTERPRISE==false)  //if W2K is defined but ENTERPRISE is not .
3.#warning和#error


当编译器遇到这两个预处理器指令时会分别产生警告或错误。当编译器遇到#warning指令,会给用户显示#warning指令后面的文本,之后编译继续进行。如果编译器遇到#error指令,就会给用户显示后面的文本,作为一条编译错误消息,然后会立即退出编译,不会产生IL代码。使用这两条指令可以检查#define语句是否做错了什么,#warning可以提醒自己都进行了哪些操作:
#if DEBUG && RELEASE
#error "已经定义了DEBUG和RELEASE"
#endif
#warning "下面是一句没用的输出"
Console.WriteLine("hello world");
4.#region和#endregion

#region和#endregion指令用于把一段代码标记为有给定名称的一个块,如下所示:
#region Member Field Declarations
int x;
double d;
Currency balance;
#endregion
这两条指令的优点是他们可以被某些编辑器识别,如Visual Studio .NET编辑器。这些编辑器可以使用这些指令使代码在屏幕上更好地布局。


5.#pragma
#lpragma指令可抑制或还原指定的编译警告。其可以在类或方法级别执行。例:
#pragma warning disable 169
public class MyClass
{
int neverUsedField;
}
#pragma warning restore 169
上面的例子禁止“字段未使用”警告,然后在编译MyClass类后还原该警告。


评分

参与人数 1技术分 +1 收起 理由
宋天琪 + 1

查看全部评分

2 个回复

倒序浏览
今晚最后一篇帖子,希望版主大大都能给个分啊,我当真想进第四期……当然,也希望别的努力的同学也都顺利通过~~
睡觉{:soso_e175:}
回复 使用道具 举报
我来个详细的:
C# 语言的预处理器指令:

#if
#else
#elif
#endif
# define
#undef
#warning
#error
#line
#region
#endregion
#pragma
#pragma warning
#pragma checksum
一、#if
         #if 使您可以开始条件指令,测试一个或多个符号以查看它们是否计算为 true。如果它们的计算结果确实为 true,则编译器将计算位于 #if 与最近的 #endif 指令之间的所有代码。以 #if 指令开始的条件指令必须用 #endif 指令显式终止。例如:


#define DEBUG
// ...
#if DEBUG
    Console.WriteLine("Debug version");
#endif


         可以使用运算符 ==(相等)、!=(不相等)、&&(与)及 ||(或)来计算多个符号。还可以用括号将符号和运算符分组。


备注:
         使用 #if 以及 #else、#elif、#endif、#define 和 #undef 指令,可以包括或排除基于由一个或多个符号组成的条件的代码。这在编译调试版本的代码或编译特定配置时最为有用。

二、#else
         #else 允许您创建复合条件指令,因此,如果前面的 #if 或(可选)#elif 指令中的任何表达式都不为 true,则编译器将计算 #else 与后面的 #endif 之间的所有代码。

备注:#endif必须是#else的下一个预处理器指令。

三、#elif

         使您得以创建复合条件指令。如果前面的 #if 和前面的任何 #elif(可选)指令表达式的计算结果都不是 true,则将计算 #elif 表达式。如果 #elif 表达式计算为 true,编译器将计算位于 #elif 和下一个条件指令之间的所有代码。

三、#endif
         #endif 指定以 #if 指令开头的条件指令的结尾。

四、#define
         使用 #define 可以定义一个符号,并通过将该符号用作表达式传递给 #if 指令,使该表达式的计算结果为 true。可以定义符号,但是无法对符号赋值。例如:

         # define DEBUG

         符号可用于指定编译的条件。可以使用 #if 或 #elif 来测试符号。还可以使用 conditional 属性执行条件编译。
         也可以用 /define 编译器选项来定义符号。可以用 #undef 来取消定义符号。 用 #define 创建的符号的范围是在其中定义该符号的文件。

备注:与C/C++不一样,C#的#define语句仅允许你在符号表中插一个标签,但你不能给任何一个符号赋一个值。与C/C++另一个不同的地方,C/C++允许#define预处理器指令出现在任何所需的地方,而在使用任何非预处理器指令的指令前,C#的#define指令必须出现在一个文件中。用#define定义的标签不会与同名的变量冲突。这就是说,一个变量名不应该传递给预处理器指令,而且符号只能通过预处理器指令来求值。插入到符号表中的标签的作用域就是定义它的文件。可以用#undef取消符号定义。

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

六、#warning
         #warning 使您得以从代码的特定位置生成一级警告。例如:
         #warning Deprecated code in this method.


备注:
#warning 通常用在条件指令中。也可以用 #error(C# 参考)生成用户定义的错误。
示例
// preprocessor_warning.cs
// CS1030 expected
#define DEBUG
class MainClass
{
    static void Main()
    {
#if DEBUG
#warning DEBUG is defined
#endif
    }
}

七、#error
         #error 使您可以从代码中的特定位置生成错误。例如:
         #error Deprecated code in this method.

八、#line
         #line 使您可以修改编译器的行号以及(可选)错误和警告的文件名输出。下面的示例说明如何报告与行号关联的两个警告。#line 200 指令强迫行号为 200(尽管默认值为 #7)。另一行 (#9) 作为默认 #line 指令的结果跟在通常序列后。


class MainClass
{
    static void Main()
    {
#line 200
        int i;    // CS0168 on line 200
#line default
        char c;   // CS0168 on line 9
    }
}


备注:
         #line 指令可能由生成过程中的自动中间步骤使用。例如,如果行从原始的源代码文件中移除,但是您仍希望编译器基于文件中的原始行号生成输出,则可以移除行,然后用 #line 模拟原始行号。 #line hidden 指令对调试器隐藏若干连续的行,这样当开发人员在逐句通过代码时,将会跳过 #line hidden 和下一个 #line 指令(假定它不是另一个 #line hidden 指令)之间的所有行。此选项也可用来使 ASP.NET 能够区分用户定义的代码和计算机生成的代码。尽管 ASP.NET 是此功能的主要使用者,但很可能将有更多的源生成器使用它。
         #line hidden 指令不会影响错误报告中的文件名或行号。即,如果在隐藏块中遇到错误,编译器将报告当前文件名和错误的行号。
         #line filename 指令指定您希望出现在编译器输出中的文件名。默认情况下,使用源代码文件的实际名称。文件名必须括在双引号 ("") 中。 源代码文件可以具有 #line 指令的任何编号。


示例
         下面的示例说明调试器如何忽略代码中的隐藏行。运行此示例时,它将显示三行文本。但是,当设置如示例所示的断点并按 F10 键逐句通过代码时,您将看到调试器忽略了隐藏行。还请注意,即使在隐藏行上设置断点,调试器仍会忽略它。
// preprocessor_linehidden.cs
using System;
class MainClass
{
    static void Main()
    {
        Console.WriteLine("Normal line #1."); //这里设置断点
#line hidden
        Console.WriteLine("Hidden line.");
#line default
        Console.WriteLine("Normal line #2.");
    }
}

九、#region
         #region 使您可以在使用 Visual Studio 代码编辑器的大纲显示功能时指定可展开或折叠的代码块。例如:
#region MyClass definition
public class MyClass
{
    static void Main()
    {
    }
}
#endregion


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

十、#endregion
#endregion 标记 #region 块的结尾。

十一、#pragma
#pragma 用于给编辑器提供特殊的指令,说明如何编译包含杂注的文件。
#pragma pragma-name pragma-arguments
参数
pragma-name
可识别杂注的名称。
pragma-arguments
杂注特定的参数。

十二、#pragma warning
#pragma warning 可用于启用或禁用某些警告。
#pragma warning disable warning-list
#pragma warning restore warning-list
参数
warning-list
警告编号的逗号分隔列表。只输入数字,不包括前缀 "CS"。
当没有指定警告编号时,disable 禁用所有警告,而 restore 启用所有警告。
示例
// pragma_warning.cs
using System;

#pragma warning disable 414, 3021
[CLSCompliant(false)]
public class C
{
    int i = 1;
    static void Main()
    {
    }
}
#pragma warning restore 3021
[CLSCompliant(false)] // CS3021
public class D
{
    int i = 1;
    public static void F()
    {
    }
}

十三、#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 文件。
示例
class TestClass
{
    static int Main()
    {
        #pragma checksum "file.cs" "{3673e4ca-6098-4ec1-890f-8fceb2a794a2}" "{012345678AB}" // New checksum
    }
}

评分

参与人数 1技术分 +2 收起 理由
宋天琪 + 2

查看全部评分

回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马