源代碼指定了程序的定義,預處理指令(preprocessor directive)指示編譯器如何處理源代碼。例如,在某些狀況下,咱們可能但願編譯器忽略一部分代碼,而在其餘狀況下,咱們可能但願代碼被編譯。預處理指令給了咱們這樣的選項。
在C和C++中有實際的預處理階段,此時預處理程序遍歷源代碼而且爲以後的編譯階段準備文本輸出流,在C#中沒有實際的預處理程序。「預處理」指令由編譯器來處理,而這個術語保留了下來。程序員
下面是預處理指令的最重要的一些語法規則。函數
這裏的一些示例闡釋了這些規則:工具
#define PremiumVersion //沒有分號 正確 #define BudgetVersion //前面有空格 正確 # define MediumVersion //中間有空格 正確 不容許分隔符註釋 ↓ #define PremiumVersion /* all bells & whistles */ 錯誤 #define BudgetVersion // Stripped-down version 容許行尾註釋 正確
編譯符號是隻有兩種可能狀態的標識符,要麼被定義,要麼未被定義。編譯符號有以下特性。ui
如上表所示: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
以下是一個條件編譯的示例: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
結構中;
#endif
以後的代碼#endif
以後的代碼#endif
以後的代碼#if...#elif...#else
結構也是相同的工做方式,只不過沒有條件是true的狀況下,會編譯#else
以後的代碼段,而後會繼續編譯#endif
以後的代碼
以下的代碼演示了#if...#elif...#else
結構。包含程序版本描述的字符串根據定義的編譯符號被設置爲各類值。
#define DemoVersionWithoutTimeLimit … const 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
指令加上一個整數參數會使編譯器認爲下面代碼的行是所設置的行,以後的行數會在這個行數的基礎上遞增。
下面的代碼給出了行號指令的示例:
#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
指令容許咱們關閉及從新開啓警告消息。
例如,下面的代碼關閉了兩個警告消息:618和414。在後面的代碼中又開啓了618警告消息,但仍是保持414消息爲關閉狀態。
要關閉的警告消息 ↓ #pragma warning disable 6l8, 414 … 列出的警告消息在這段代碼中處於關閉狀態 #pragma warning restore 618
若是咱們使用任一種不帶警告數字列表的形式,這個命令會應用於全部警告。例如,下面的代碼關閉,而後恢復全部警告消息。
#pragma warning disable … 全部警告消息在這段代碼中處於關閉狀態 #pragma warning restore … 全部譬告消息在這段代碼中處於開啓狀態