<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # 原則4:使用條件特性(conditional attribute)代替 #if **By D.S.Qiu** 尊重他人的勞動,**支持原創,轉載請注明 [出處](/blog/1979093) :[http://dsqiu.iteye.com](http://dsqiu.iteye.com)** \#if/\#endif 塊在相同的代碼會編譯成不同的版本,大多數會有調試(debug)和發布(release)兩個版本。但我們會很不樂意去使用這個工具。 \#if/\#endif 塊很容易被濫用,寫的代碼很難理解而且不容易調試。語言設計者應該負責設計能在不同環境產生不同的機器碼的工具。 C# 提供了條件特性(conditional attribute),可以根據設置的環境決定函數的調用。使用條件特性會使用 #if/endif 更加清晰。編譯器能解析條件特性,所以能很好的確定那段代碼被調用。條件特性是應用在函數級別的,所以你應該分開不同的條件代碼封裝成不同的方法。當你要使用條件代碼塊的時候,使用條件特性代替 #if/endif 。 很多資深的程序員喜歡使用條件編譯來檢查對象的先決和后續條件。你會寫一個 private 方法去檢查所有類型和對象變量。這個方法使用了條件編譯以至于只能在調試版本出現。 ``` private void CheckStateBad() { // The Old way: #if DEBUG Trace.WriteLine("Entering CheckState for Person"); // Grab the name of the calling routine: string methodName = new StackTrace().GetFrame(1).GetMethod().Name; Debug.Assert(lastName != null, methodName, "Last Name cannot be null"); Debug.Assert(lastName.Length > 0, methodName, "Last Name cannot be blank"); Debug.Assert(firstName != null, methodName, "First Name cannot be null"); Debug.Assert(firstName.Length > 0, methodName, "First Name cannot be blank"); Trace.WriteLine("Exiting CheckState for Person"); #endif } ``` 上面方法使用 #if 和 #endif 編譯選項,你會發現在發布版本你實際創建了一個空的方法。 CheckState() 會被所有版本中調用,比如發布版本和調試版本。在發布版本沒有做任何事情,但你為此付出了函數調用的代價。同時還要有很小花費在加載和 JIT 空程序。 這個實踐是正確的,但在發行版本會導致一個微妙的錯誤。下面這個就是使用條件編譯選項的常見錯誤: ``` public void Func() { string msg = null; #if DEBUG msg = GetDiagnostics(); #endif Console.WriteLine(msg); } ``` 在調試版本會正確工作,但是你的發行版本只會讓你哭笑不得地輸出空字符串。當然這不是你想要的。你出了錯,編譯器就幫不了你了。在條件編譯器塊中的代碼就是你的邏輯。在 #if/endif 塊中的代碼很難讓你診斷出不同版本的不同行為。 C# 有一個更好的選擇:條件特性。使用條件特性,能分離出不同函數,只有在特定的環境變量的定義或某些值的設置才會屬于你的類。這個功能最常見的好處就是在調試的時候能有可用的聲明。 .NET 框架已經提供了基本的通用功能。這個例子告訴我們 .NET 框架的調試功能,已經條件特性是怎么工作的和什么時候添加到你的代碼中。 當你創建了 Person 對象,你要寫一個方法去檢查對象的變量: ``` private void CheckState() { // Grab the name of the calling routine: string methodName = new StackTrace().GetFrame(1).GetMethod().Name; Trace.WriteLine("Entering CheckState for Person:"); Trace.Write("\tcalled by "); Trace.WriteLine(methodName); Debug.Assert(lastName != null, methodName, "Last Name cannot be null"); Debug.Assert(lastName.Length > 0, methodName, "Last Name cannot be blank"); Debug.Assert(firstName != null, methodName, "First Name cannot be null"); Debug.Assert(firstName.Length > 0, methodName, "First Name cannot be blank"); Trace.WriteLine("Exiting CheckState for Person"); } ``` 我簡化了這個方法以至于沒有用太多類庫的函數。 StackTrace 類使用反射獲取正在調用函數的名字。這是消耗性能的,但這簡化了工作,比如產生了程序流程的信息。上面代碼,檢測到調用函數的名字是 CheckState 。如果被 inline 調用會有一個小的風險,另一種方法就是在調用 CheckState 函數的方法使用 MethodBase.GetCurrentMethod() 傳入方法名。你很快會明白為什么不使用這個策略。 后面的方法是 System.Diagnositics.Debug 類或 System.Diagnostics.Trace 類的函數。 Debug.Assert 測試條件,且當條件為 false 是程序停止。后面的是條件為 false 是打印出來的信息。 Trace.WriteLine 將診斷信息輸出到調試控制臺上。所以這個方法實際當 person 對象不正確是輸出信息并終止程序。你可以在所有 public 方法或屬性中調用這個方法作為先決條件和后續條件: ``` public string LastName { get { CheckState(); return lastName; } set { CheckState(); lastName = value; CheckState(); } } ``` 如果將一個空字符串或 null 賦給 lastName , CheckState 觸發一個斷言(assert)。然后檢驗 lastName 的值。這就是你想要做的。 但這額外的檢查會在每個例行任務中花費時間。你只是在調試版本中需要額外的檢查。那就是為什么會有條件特性: ``` [Conditional("DEBUG")] private void CheckState() { // same code as above } ``` 條件特性告訴 C# 編譯器這個方法只能在有 DEBUG 變量的環境中被調用。 條件特性不影響 CheckState 函數代碼的產生,修改的是調用者的代碼。如果 DEBUG 變量被定義,你的代碼是這樣的: ``` public string LastName { get { CheckState(); return lastName; } set { CheckState(); lastName = value; CheckState(); } } ``` 如果沒有被定義,會是這樣的: ``` public string LastName { get { return lastName; } set { lastName = value; } } ``` 無論環境變量狀態是怎么樣的, CheckState 函數體都是一樣的。這就是要告訴我們, .NET 的編譯和 JIT 之間的區別。不管 DEBUG 環境環境變量是否定義, CheckState() 方法都會被編譯嵌入在程序集中。這可能不是很搞笑,但這只是花費了硬盤的容量。 CheckState() 不會被載入內存和 JITed ,除非被調用。它存在在程序集的二進制文件中是無關緊要的。這個策略增加靈活性而且不消耗性能。閱讀 .NET 框架的 Debug 類可以有更加深入的理解。安裝了 .NET 框架的機器, System.dll 程序集會有 Debug 類的所有方法代碼。當調用者函數被編譯,環境變量控制方法是否被調用。運用條件指令可以讓你創建的類庫嵌入調試特性。這些特性可以運行時打開或關閉。 你可以創建一個方法依賴多個環境變量。當你運用多個條件特性,它們是通過 OR 組合起來的。例如,下面版本的 CheckState 當 DEBUG 或 TRACE 為真時,會調用。 ``` [Conditional("DEBUG"),Conditional("TRACE")] private void CheckState() ``` 如果想要使用 AND 構建,你需要使用預處理指令定義預處理符: ``` #if ( VAR1 && VAR2 ) #define BOTH #endif ``` 的確,當你要創建一個依賴多于一個環境變量的條件行為,你不得不退回到之前 #if 的做法。所有 #if 創建一個符號。但要避免在編譯選項中加入任何可運行代碼。 所以,你可以按下面方式重寫 CheckState 方法: ``` private void CheckStateBad() { // The Old way: #if BOTH Trace.WriteLine("Entering CheckState for Person"); // Grab the name of the calling routine: string methodName = new StackTrace().GetFrame(1).GetMethod().Name; Debug.Assert(lastName != null, methodName, "Last Name cannot be null"); Debug.Assert(lastName.Length > 0, methodName, "Last Name cannot be blank"); Debug.Assert(firstName != null, methodName, "First Name cannot be null"); Debug.Assert(firstName.Length > 0, methodName, "First Name cannot be blank"); Trace.WriteLine("Exiting CheckState for Person"); #endif } ``` 條件特性只能運用于整個的方法。除此之外,任何條件特性方法必須是 void 返回值。你在代碼塊中使用條件特性,也不能創建有返回值的條件特性方法。而是,要分離出具體條件行為的代碼單獨寫成條件特性方法。你應該回顧下那些條件方法對對象狀態的副作用,條件特性會比 #if/endif 好很多。使用 #if/endif 塊,你會錯誤的移除了很多重要的方法調用或賦值語句。 前面的例子使用預定義的 DEBUG 或 TRACE 符號。你也可以擴展任何你定義的符號。條件特性可以被多種方式定義的符號控制。你可以定義符號從編譯命令行,或從操作系統 shell 的環境變量,或從代碼的編譯選項中定義。 你應該注意到前面的每個條件特性的方法都是 void 返回值而且沒有參數。實際實踐應該遵從這個原則。編譯器是前置條件特性方法必須是 void 返回值。然后,你可以創建一個方法含有引用類型參數。這種做法會導致副作用,應該盡量避免。考慮下面一段代碼: ``` Queue<string> names = new Queue<string>(); names.Enqueue("one"); names.Enqueue("two"); names.Enqueue("three"); string item = string.Empty; SomeMethod(item = names.Dequeue()); Console.WriteLine(item); ``` SomeMethod 添加了條件特性: ``` [Conditional("DEBUG")] private static void SomeMethod(string param) { } ``` 這里會有一個很微妙的錯誤。 SomeMethod() 只有在 DEBUG 符號被定義了才會被調用。而且 names.Dequeue() 也是一樣的。因為結果不是必須的,所以方法沒有調用。任何條件特性的方法不應該有任何參數。使用調用方法來產生參數會有副作用。如果條件不為 ture 這些方法不會被調用。 條件特性比 \#if/\#endif 產生了更高效的 IL 代碼。還有一個好處就是只能使用在函數級別上,這迫使你要更好的組織你的條件代碼。編譯器使用條件特性幫助我們避免了使用 \#if/\#endif 的常見錯誤。條件特性比預處理更能讓你你的條件代碼分離的更清晰。 小結: 更新晚了,昨天晚上寫了一半,現在弄完。昨天家里發生了點事情,心里一直不安,感覺挺無奈的。只有對自己說,我要努力,我要頂住。原則4,相對于前面3個原則有點偏門,而且兩點少了點,說服力不夠。 歡迎各種不爽,各種噴,寫這個純屬個人愛好,秉持”分享“之德! 有關本書的其他章節翻譯請[點擊查看](/category/297763),轉載請注明出處,尊重原創! 如果您對D.S.Qiu有任何建議或意見可以在文章后面評論,或者發郵件(gd.s.qiu@gmail.com)交流,您的鼓勵和支持是我前進的動力,希望能有更多更好的分享。 轉載請在**文首**注明出處:[http://dsqiu.iteye.com/blog/1979093](/blog/1979093) 更多精彩請關注D.S.Qiu的 博客 和微博(ID:靜水逐風)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看