当前位置: 首页 > news >正文

C# 预处理指令

一、什么是预处理指令

  源代码指定了程序的定义,预处理指令(preprocessor  directive)指示编译器如何处理源代码。例如,在某些情况下,我们希望编译器能够忽略一部分代码,而在其他情况下,我们希望代码被编译,这时我们就可以使用预处理指令了。

  C++开发人员应知道,在C 和C++中预处理器指令非常重要,但是,在C#中,并没有那么多的预处理器指令,它们的使用也不太频繁。C#提供了其他机制来实现许多C++指令的功能,如定制特性。还要注意,C#并没有一个像C++那样的独立预处理器,所谓的预处理器指令实际上是由编译器处理的。尽管如此,C#仍保留了一些预处理器指令名称,因为这些命令会让人觉得就是预处理器。

 

二、基本规则

  知道了什么是预处理指令,那么如何使用它呢?下面是它的一些重要语法规则

  ♥  预处理指令必须和C#代码在不同的行  

  ♥  与C#语句不同,预处理指令不需要以分号结尾

  ♥  包含预处理指令的每一行必须与 ‘’#‘’ 字符开始(在#字符前可以有空格,在#字符和指令之间也可以有空格)

  ♥  允许行尾注释

  ♥  在预处理指令所在的行不允许有分隔符注释

  讲了这么多,举个栗子

#define Premium     //结尾没有分号,可以有行尾注释,不允许分隔符注释,即不能有如下类型的注释/*...*/
    #define Budget      //前面可以有空格
# define Medium         // 中间可以有空格

 

三、内容详解

  下表列出了预处理指令及其含义概要

指令含义概要
#define  identifier定义编译符
#undef  identifier取消编译符
#if expression如果表达式为true,编译下面的片段
#elif expression如果表达式为true,编译下面的片段
#else如果之前的#if或#elif表达式是false,编译下面的片段
#endif标记为一个#if结构的结束
#region name标记一段代码的开始,没有编译效果
#endregion name标记一段代码的结束,没有编译效果
#warning message显式编译时的警告消息
#error message 显式编译时的错误消息
#line indicator 修改在编译器消息中显式的行数
#pragma text指定有关程序上下文的信息

  详细解释上面指令的用法

  3.1  #define和#undef指令

    编译符号是只有两种可能状态的标识符,要么被定义,要么未被定义,它可以是除了true或false以外的任何标识符,包括C#关键字

    #define 的用法如下所示: #define DEBUG

    它告诉编译器存在给定名称的符号,在本例中是DEBUG。这有点类似于声明一个变量,但这个变量并没有真正的值,不表示字符串,只是存在而已。

    这个符号不是实际代码的一部分,而只在编译器编译代码时存在。在C#代码中它没有任何意义。


    #undef 正好相反—— 它删除符号的定义: #undef DEBUG

    如果符号不存在,#undef 就没有任何作用。同样,如果符号已经存在,则#define 也不起作用,允许重复定义已存在的编译符号。必须把#define 和#undef 命令放在C#源文件的开头位置,在声明要编译的任何对象的代码之前。

    #define 本身并没有什么用,但与其他预处理器指令(特别是#if)结合使用时,它的功能就非常强大了。

    这里应注意一般C#语法的一些变化。预处理器指令不用分号结束,一般一行上只有一条命令。这是因为对于预处理器指令,C#不再要求命令使用分号进行分隔。如果它遇到一条预处理器令,  就会假定下一条命令在下一行上。

 

  3.2  条件编译(#if、#elif、#else 和#endif

    条件编译允许我们根据某个编译符号是否被定义标注一段代码编译或跳过,条件是一个返回true或false的简单表达式

参数类型意义运算结果
编译符号使用#define指令(未)定义的标识符

true:标识符已使用#define定义

false:其他

表达式使用符号和操作符!、==、!=、&&、|| 构建的

true:如果表达式运算结果为true

false:其他

    如上图所示,在#if和#elif指令中使用的条件可以由单个编译符号、符号表达式或操作符组成。子条件可以使用圆括号分组。  文本true或false也可以在条件表达式中使用

#define Premium     
#define Budget               

using System;

namespace 预处理指令
{
    class Program
    {
        static void Main(string[] args)
        {
#if Premium&&Budget
            Console.WriteLine("defined!");    //输出defined!
#endif

#if Hello
            Console.WriteLine("defined!");    //Hello未被定义,不输出
#else
            Console.WriteLine("undefined!");    //输出undefined!
#endif
            Console.ReadKey();
        }
    }
}

  当编译器遇到#if 语句后,将先检查相关的符号是否存在,如果符号存在,就编译#if 子句中的代码。否则,编译器会忽略所有的代码,直到遇到匹配的#endif 指令为止。

  #elif (=else if),其用法及含义十分直观,就不举例子了

  值得注意的是,#if和#endif指令在条件编译结构中必须配对使用,只要有#if指令,就必须有#endif配对

 

  3.3  诊断指令(#warning和#error)

    诊断指令产生用户自定义的编译时的警告或错误消息,它的用法为

#warning Message

Message是字符串,它与普通的C#字符串不同,不需要被引号包围

#define Premium     
#define Budget               

using System;
namespace 预处理指令
{
    class Program
    {
        static void Main(string[] args)
        {
#if Premium && Budget
#error   Can't build this code
#endif
#warning Remember to come back to check this code
            Console.ReadKey();
        }
    }
}

可以看到,编译器中出现了对应的错误和警告提醒

 

  3.4  行号指令(#line)

     行号指令可以做许多事,例如

       ♥ 改变由编译器警告和错误消息报告的出现行数

    ♥ 改变被编译源文件的文件名

    ♥ 对交互式调试器隐藏一些行

class MainClass  
{  
    static void Main()  
    {  
#line 200 "Special"      //设置下一行的行号值为200,并将编译源文件的名字改成“Special”
        int i;
        int j;
#line default    //重新保存实际的行号和文件名
        char c;
        float f;
#line hidden // 在断点调试器中隐藏代码  
        string s;   
        double d;
#line  //停止在调试器中隐藏代码 } }

编译产生以下输出

Special(200,13): warning CS0168: The variable 'i' is declared but never used
Special(201,13): warning CS0168: The variable 'j' is declared but never used
MainClass.cs(9,14): warning CS0168: The variable 'c' is declared but never used
MainClass.cs(10,15): warning CS0168: The variable 'f' is declared but never used
MainClass.cs(12,16): warning CS0168: The variable 's' is declared but never used
MainClass.cs(13,16): warning CS0168: The variable 'd' is declared but never used

 仔细观察输出结果你将可以发现其中的不同,使用时要注意的是:

  要改变外观文件名,可以在双引号内使用文件名作为参数,双引号是必须的

  要返回真是的文件名和行号,可以使用default参数

  要对交互调试器的断点调试功能隐藏代码段,可以使用hidden参数。要停止隐藏,可以使用不带任何参数的指令

  #line hidden 指令能对调试程序隐藏连续行,当开发者逐行执行代码时,介于 #line hidden 和下一 #line 指令(假设它不是其他 #line hidden 指令)间的任何行都将被跳过。它不影响错误报告中的文件名或行号。 也就是说,如果在隐藏块中遇到错误,编译器将报告错误的当前文件名和行号。

 

  3.5  区域指令(#region和#endregion)

    利用 #region,可以指定在使用 Visual Studio Code 编辑器的大纲功能时可展开或折叠的代码块。 在较长的代码文件中,能够折叠或隐藏一个或多个区域会十分便利,这样,可将精力集中于当前处理的文件部分。 

#define Premium     
#define Budget               

using System;

namespace 预处理指令
{
    #region program  //名字为program
    class Program
    {
        static void Main(string[] args)
        {
#line 89 "special"
            int c;
#line default
#if Budgets
            Console.WriteLine("defined!");
#else
            Console.WriteLine("undefined!");
#endif
            Console.ReadKey();
        }
    }
#endregion  //结束标记
}

在使用#region的地方出现了可以折叠的标志

备注:

#region 块必须通过 #endregion 指令终止。

#region 块不能与 #if块重叠。 但是,可以将 #region 块嵌套在 #if 块内,或将 #if 块嵌套在 #region 块内。

#region指令后的可选字符串文本作为其名字  

 

  3.6  #pragma warning指令

    #pragma warning指令允许我们关闭或重新开启warning指令

    要关闭警告消息,可以使用disable加上逗号分隔希望关闭的警告数列表

    要开启警告消息,可以使用restore加上逗号分隔希望开启的警告数列表

using System;  
  
#pragma warning disable 414   //关闭414行的警告

public class C  
{  
    int i = 1;  
    static void Main()  
    {  
    }  
}  
#pragma warning restore   //所有警告消息在下面代码中处于开启状态

public class D  
{  
    int i = 1;  
    public static void F()  
    {  
    }  
}

 

           如果还有梦就追,至少不会遗憾后悔!

 

转载于:https://www.cnblogs.com/forever-Ys/p/10372203.html

相关文章:

  • 安装Asp.net 2.0服务器出现Server Application Unavailabl --zt
  • 解决FirewallD is not running问题
  • 使用Collectd + InfluxDB + Grafana进行JMX监控
  • 通过自己的项目实际经验,阐述为什么“恶心玩技术”?玩Java技术的教训(一)...
  • centos 生成网卡UUID
  • repo源及yum的常用方法
  • Python Django 初试手记
  • 线性表-顺序存储
  • c# xml读写 操作 实例
  • 设计 MySQL 数据表的时候一般都有一列为自增 ID,这样设计原因是什么,有什么好处?...
  • 买盘+卖盘≠成交量
  • 主题模型--机器学习
  • 初探C#3.0
  • (6)设计一个TimeMap
  • 20190218日记
  • 「译」Node.js Streams 基础
  • Linux后台研发超实用命令总结
  • Linux快速复制或删除大量小文件
  • Mithril.js 入门介绍
  • TypeScript实现数据结构(一)栈,队列,链表
  • 大快搜索数据爬虫技术实例安装教学篇
  • 发布国内首个无服务器容器服务,运维效率从未如此高效
  • 快速构建spring-cloud+sleuth+rabbit+ zipkin+es+kibana+grafana日志跟踪平台
  • 设计模式(12)迭代器模式(讲解+应用)
  • 实现简单的正则表达式引擎
  • 我的zsh配置, 2019最新方案
  • # Swust 12th acm 邀请赛# [ A ] A+B problem [题解]
  • #WEB前端(HTML属性)
  • (4.10~4.16)
  • (pojstep1.1.1)poj 1298(直叙式模拟)
  • (附源码)php新闻发布平台 毕业设计 141646
  • (附源码)计算机毕业设计ssm高校《大学语文》课程作业在线管理系统
  • (离散数学)逻辑连接词
  • (七)Java对象在Hibernate持久化层的状态
  • (四)【Jmeter】 JMeter的界面布局与组件概述
  • (转)http-server应用
  • (自用)learnOpenGL学习总结-高级OpenGL-抗锯齿
  • .net core开源商城系统源码,支持可视化布局小程序
  • .NET 的程序集加载上下文
  • @取消转义
  • [ vulhub漏洞复现篇 ] Django SQL注入漏洞复现 CVE-2021-35042
  • [100天算法】-实现 strStr()(day 52)
  • [28期] lamp兄弟连28期学员手册,请大家务必看一下
  • [BUG] Authentication Error
  • [C# WPF] DataGrid选中行或选中单元格的背景和字体颜色修改
  • [CC-FNCS]Chef and Churu
  • [CF407E]k-d-sequence
  • [DL]深度学习_Feature Pyramid Network
  • [EFI]DELL XPS13 9360电脑 Hackintosh 黑苹果efi引导文件
  • [EULAR文摘] 脊柱放射学持续进展是否显著影响关节功能
  • [ExtJS5学习笔记]第三十节 sencha extjs 5表格gridpanel分组汇总
  • [Google Guava] 1.1-使用和避免null
  • [iOS开发]iOS中TabBar中间按钮凸起的实现
  • [LeetCode] NO. 169 Majority Element
  • [LeetCode]--61. Rotate List