文章目錄
- 使用#if/#endif 語句的缺點
- 更好的解決辦法——使用Conditional特性
- Conditional特性的限制
#if/#endif 語句常用來基於同一份源碼產生不同的編譯結果,其中最常見的就是debug版和release版。但是這些工具在實際應用中並不是非常友好,因為它們容易被濫用,其字碼頁進而難以理解或調試。C#設計中考慮到這個問題,並提供了更好的工具——Conditional特性,用來為不同的環境編譯不同的機器碼。Conditional特性適用於方法的層面,這將強制我們將條件代碼拆分為獨立的方法。在需要編寫條件代碼時,我們應該使用Conditional特性來替代#if/#endif。
使用#if/#endif 語句的缺點
例如編寫一個私人方法來擷取調用它的函數名稱:
1 private string CheckMethod() 2 { 3 4 #if DEBUG 5 Trace.WriteLine("Entering CheckState for Person"); 6 7 string methodName = new StackTrace().GetFrame(1).GetMethod().Name; 8 9 return methodName;10 #endif11 return null;12 }
條件編譯#if和#endif將會在最終release版本中留下一個名為CheckMethod()的空方法,但它在release版和debug版中都將被調用,雖然在release版本中CheckMethod()什麼也不做,但是方法的載入、JIT編譯和調用仍舊有些開銷。而且這也容易引入一些問題:
1 public void Func()2 {3 string msg = null;4 5 #if DEBUG6 msg = "Debug";7 #endif8 Console.WriteLine(msg);9 }
這段代碼在Debug版本中不會有問題,但是在release版本中就會輸出一個空行,出錯的原因是因為我們把屬於主程式的邏輯和條件編譯的邏輯混合在一起了。在原始碼中隨意使用#if和#endif將讓你很難診斷出不同版本之間的差異。
更好的解決辦法——使用Conditional特性
為了避免出現上面的問題我們可以使用Conditional特性。使用Conditional特性即可將一些函數拆分出來,讓其只有在定義了某些環境變數或者設定了某個值之後才能編譯並成為類的一部分。Conditional特性最常用的地方就是講一段代碼變成調試語句。使用Conditional特性的隔離策略要比#if/#endif不容易出錯。
看下面的代碼:
1 [Conditional("DEBUG")]2 private void CheckMethod()3 {4 Trace.WriteLine("Entering CheckState for Person");5 string methodName = new StackTrace().GetFrame(1).GetMethod().Name;6 }
無論是否定義了DEBUG環境變數,CheckMethod()方法都將被編譯到程式集中。但是如果沒有被調用,CheckMethod()方法並不會被載入到記憶體中,也不會被JIT編譯。這其實也揭示了C#編譯器的編譯過程與JIT編譯座次之間的區別。
Conditional特性的限制
Conditional特性只可以應用在整個方法上。
任何使用了Conditional特性的方法都只能返回void類型。
小節:
綜上所述,使用Conditional特性產生的IL要比使用#if/#endif時更有效率。同時,將其限制在函數層面上可以更清晰的將條件性代碼分離出來,以便進一步保證代碼的良好結構。此外C#編譯器也為此提供良好的支援,從而避免了以前使用#if/#endif時常犯的錯誤。與預先處理指令相比,Conditional特性讓我們可以更好地將條件性代碼分離開來。
閱讀書目:《Effective C#》