C#圖解教程 第二十三章 預處理指令

預處理指令

什麼是預處理指令


源代碼指定了程序的定義,預處理指令(preprocessor directive)指示編譯器如何處理源代碼。例如,在某些狀況下,咱們可能但願編譯器忽略一部分代碼,而在其餘狀況下,咱們可能但願代碼被編譯。預處理指令給了咱們這樣的選項。
在C和C++中有實際的預處理階段,此時預處理程序遍歷源代碼而且爲以後的編譯階段準備文本輸出流,在C#中沒有實際的預處理程序。「預處理」指令由編譯器來處理,而這個術語保留了下來。程序員

基本規則


下面是預處理指令的最重要的一些語法規則。函數

  • 預處理指令必須和C#代碼在不一樣的行
  • 與C#語句不一樣,預處理指令不須要以分號結尾
  • 包含預處理指令的每一行必須以#字符開始
    • 在#字符前能夠有空格
    • 在#字符和指令之間能夠有空格
  • 容許行尾註釋
  • 在預處理指令所在的行不容許分隔符註釋

這裏的一些示例闡釋了這些規則:工具

#define PremiumVersion     //沒有分號 正確
  #define BudgetVersion    //前面有空格 正確
# define MediumVersion     //中間有空格 正確
                                  不容許分隔符註釋
                                        ↓
#define PremiumVersion     /* all bells & whistles */ 錯誤

#define BudgetVersion      // Stripped-down version 容許行尾註釋 正確

#define和#undef指令


編譯符號是隻有兩種可能狀態的標識符,要麼被定義,要麼未被定義。編譯符號有以下特性。ui

  • 它能夠是除了true或false之外的任何標識符,包括C#關鍵字,以及在C#代碼中聲明的標識符,這二者都是能夠的
  • 它沒有值。與C和C++不一樣,它不表示字符串

如上表所示:this

  • #define指令聲明一個編譯符號
  • #undef指令取消定義一個編譯符號
#define PremiumVersion
#define EconomyVersion#undef PremiumVersion

#define#undef指令只能用在源文件的第一行,也就是任何C#代碼以前使用。在C#代碼開始後,#define#undef指令就不能再使用。spa

using System;    // C#代碼的第1行
#define PremiumVersion    //錯誤
namespace Eagle
{
    #define PremiumVersion    //錯誤

編譯符號的範圍被限制於單個源文件。只要編譯符號在任何C#代碼以前,重複定義已存在的編譯符號也是容許的。3d

#define AValue
#define BValue

#define AValue    //重複定義

條件編譯


條件編譯容許咱們根據某個編譯符號是否被定義標註一段代碼被編譯或跳過。
有4個指令能夠用來指定條件編譯:調試

  • #if
  • #else
  • #elif
  • #endif

條件是一個返回true或false的簡單表達式。rest

  • 以下表所總結的,條件能夠由單個編譯符號、符號表達式或操做符組成。子條件可使用圓括號分組
  • 文本true或false也能夠在條件表達式中使用


以下是一個條件編譯的示例:code

      表達式
         ↓
#if !DemoVersion#endif
                 表達式
                    ↓
#if(LeftHanded && OemVersion)||完整版#endif

#if true //下面的代碼片斷老是會被編譯#endif

條件編譯結構


指令在條件編譯結構中須要配對使用。只要有#if指令,就必須有配對的#endif#if#if ...#else結構以下圖所示。

  • 若是#if結構中的條件運算結果爲true,隨後的代碼段就會被編譯,不然就會被跳過
  • #if...#else結構中,若是條件運算結果爲true,CodeSection1就會被編譯,不然,CodeSection2會被編譯

例如,以下的代碼演示了簡單的#if...#else結構。若是符號RightHanded被定義了,那麼#if#else之間的代碼會被編譯。不然,#else#endif之間的代碼會被編譯。

#if RightHanded
    //實現右邊函數的代碼
#else
    //實現左邊函數的代碼
#endif

下圖演示了#if ...#elif以及#if ...#elif...#else的結構。

  • #if...#elif結構中;
    • 若是Cond1運算結果爲true,CodeSectionl就會被編譯,而後就會繼續編譯#endif以後的代碼
    • 不然,若是Cond2運算結果爲true,CodeSection2就會被編譯,而後會繼續編譯#endif以後的代碼
  • 直到條件運算結果爲true或全部條件都返回false,若是這樣,結構中沒有任何代碼段會被編譯,會繼續編譯#endif以後的代碼
  • #if...#elif...#else結構也是相同的工做方式,只不過沒有條件是true的狀況下,會編譯#else以後的代碼段,而後會繼續編譯#endif以後的代碼


以下的代碼演示了#if...#elif...#else結構。包含程序版本描述的字符串根據定義的編譯符號被設置爲各類值。

#define DemoVersionWithoutTimeLimitconst int intExpireLength = 30;
    string strVersionDesc     = null;
    int    intExpireCount     = 0;

#if DemoVersionWithTimeLimit
    intExpireCount = intExpireLength;
    strVersionDesc = "This version of Supergame Plus will expire in 30 days";
#elif DemoVersionWithoutTimeLimit
    strVersionDesc = "Demo Version of Supergame Plus";
#elif OEMVersion
    strVersionDesc = "Supergame Plus, distributed under license";
#else
    strVersionDesc = "The original Supergame Plus!!";
#endif
    Console.WriteLine( strVersionDesc );
        …

診斷指令


診斷指令產生用戶自定義的編譯時警告及錯誤消息。
下面是診斷指令的語法。Message是字符串,可是須要注意,與普通的C#字符串不一樣,它們不須要被引號包圍。

#warning Message
#error Message

當編譯器遇到診斷指令時,它會輸出相關的消息。診斷指令的消息會和任何編譯器產生的警告和錯誤消息列在一塊兒。
例如,以下代碼顯式了一個#error指令和一個#warning指令。

  • #error指令在#if結構中,閃此只有符合#if指令的條件時纔會生成消息
  • #warning指令用於提醒程序員回頭來清理這段代碼
#define RightHanded
#define LeftHanded

#if RightHanded && LeftHanded
#error Can't build for both RightHanded and LeftHanded
#endif

#warning Remember to come back and clean up this code!

行號指令


行號指令能夠作不少事情,諸如:

  • 改變由編譯器警告和錯誤消息報告的出現行數
  • 改變被編譯源文件的文件名
  • 對交互式調試器隱藏一些行

#line指令的語法以下:

#line integer    //設置下一行值爲整數的行的行號
#line "filename" //設置文件名
#line default    //從新保存實際的行號和文件名

#line hidden     //在斷點調試器中隱藏代碼
#line            //中止在調試器中隱藏代碼

#line指令加上一個整數參數會使編譯器認爲下面代碼的行是所設置的行,以後的行數會在這個行數的基礎上遞增。

  • 要改變外觀文件名,能夠在雙引號內使用文件名做爲參數。雙引號是必需的
  • 要返回真實行號和真實文件名,可使用default參數
  • 要對交互調試器的斷點調試功能隱藏代碼段,可使用hidden做爲參數。要中止隱藏,可使用不帶任何參數的指令。到目前爲止,這個功能大多用於在ASP.NET和WPF中隱藏編譯器生成的代碼。

下面的代碼給出了行號指令的示例:

#line 226
    x=y+z;               //編譯器將執行第226行
#line 330 "SourceFile.cs"//改變報告的行號和文件名
    var1=var2+var3;
    …
#line default            //從新保存行號和文件名

區域指令


區域指令容許咱們標註和有選擇性地命名一段代碼。#region指令的特性以下:

  • 被放置在但願標註的代碼段之上
  • 用指令後的可選字符串文本做爲其名字
  • 在以後的代碼中必須由#endregion指令終止

儘管區域指令被編譯器忽略,但它們能夠被源代碼工具所使用。例如,Visual Studio容許咱們很簡單地隱藏或顯式區域。
做爲示例,下面的代碼中有一個叫作Constructors的區域,它封閉了MyClass類的兩個構造函數。在Visual Studio中,若是不想看到其中的代碼,咱們能夠把這個區域摺疊成一行,若是又想對它進行操做或增長另一個構造函數,還能夠擴展它。

#region Constructors
    MyClass()
    {
        …
    }
    MyClass(string s)
    {
        …
    }
#endregion

以下圖所示,區域能夠被嵌套。

#pragma warning 指令

#pragma warning指令容許咱們關閉及從新開啓警告消息。

  • 要關閉瞀告消息,可使用disable加上逗號分隔的但願關閉的警告數列表的形式
  • 要從新開啓警告消息,可使用restore加上逗號分隔的但願關閉的警告數列表的形式

例如,下面的代碼關閉了兩個警告消息:618和414。在後面的代碼中又開啓了618警告消息,但仍是保持414消息爲關閉狀態。

                      要關閉的警告消息
                            ↓
#pragma warning disable 6l8, 414
    …    列出的警告消息在這段代碼中處於關閉狀態
#pragma warning restore 618

若是咱們使用任一種不帶警告數字列表的形式,這個命令會應用於全部警告。例如,下面的代碼關閉,而後恢復全部警告消息。

#pragma warning disable
    …    全部警告消息在這段代碼中處於關閉狀態

#pragma warning restore
    …    全部譬告消息在這段代碼中處於開啓狀態
相關文章
相關標籤/搜索