本文將為大家介紹一下如何對
C#程式Bug修複的快速方法。.NET Framework中包含許多工具可以用來更快、更容易地編寫正確的程式。但我們得面臨這樣的情況:出現bugs。不管程式多麼簡單,程式員都可能出錯。
根據我的經驗,大多數程式的bugs出現在程式員之間的介面:當一個程式員編寫的代碼被另一個程式員調用時。不知何故,調用者破壞了代碼編寫時做的假設。是誰的過錯呢?這並不要緊,更重要的是你能多快修好它?下面這些技巧將幫你在程式投入使用前更快地發現並解決這些問題。最終,這些技巧會幫你診斷任何的確在使用中出現的問題。
測試假設條件
測試假設條件是構建正確的程式最重要的一個方法。在你寫一個函數時,你應該考慮並確定你對那個函數做了什麼樣的假設。你應該問自己以下這些問題:
1. 當這個函數被調用時,這個對象必須是怎樣的(對象初試化,某個內在變數值)?
2. 當這個函數存在時,這個對象將會怎樣(仍是#1,但包括該函數的副作用)?
3. 該函數的任何參數必須是怎樣的(允許空值嗎,輸入值的範圍是什麼)?
4. 傳回值必須是怎樣的?
一旦你問了自己這四個問題並作出回答後,把答案放到代碼中。
在C#中,用System.Diagnostics.Debug類的Assent方法來表示:
以下是引用片段: public bool ProcessIterations (int numIters) { Debug.Assert (numIters > 0, "ProcessIterations.", "Iterations must be more than 0"); // More code... |
該程式碼片段執行了numiters參數必須大於零這樣一個假設。如果你用一個無效的參數調用processiterations,該assert被觸發。這時候,程式停止運行並通知使用者出現的錯誤。聲明(assertions)只被編譯到debug 版本中的程式,所以它們不影響生產情況中的效能。
為什麼用這種方法? 運用這種技巧可以確保很快地發現對你的類的方法未預料地使用。然後,或者調用者修改他的代碼,或者要求在你的類的行為(behavior)中修改。
驗證完整性
一個C# 程式中的大多數函數都是一個對象上的執行個體方法。對任何對象的有效狀態都有暗示的假設。當一個公有的方法被調用時,你應該確保那些暗示的假設經過了測試。C# 的條件編譯特徵使這一點很容易實現。
首先,寫一個私用的函數來測試對象的完整性。你在這麼做時,將該方法標為“conditional”: [Conditional ("DEBUG")]
以下是引用片段: private void ImOK () { Debug.Assert (this != null, "Testing Object State", "this cannot be null"); // More here. } |
然後,在每個公有的方法中,調用ImOK方法:
以下是引用片段: public bool ProcessIterations (int numIters) { ImOK (); Debug.Assert (numIters > 0, "ProcessIterations.", "Iterations must be more than 0");} |
在發布(Release)版本中,編譯器自動取消對ImOK 的調用。
為什麼用這種方法?運用該技巧,你可以快速發現任何你的對象狀態變得無效的情況。
運用debug 和Trace 輸出
列印診斷訊息可以幫你確定你的程式是怎麼出錯的。你需要知道當觸發一個Assert時,發生了什麼情況;你也通常需要知道在這之前發生了什麼。知道這些的最好的方式就是運用你的代碼,這樣你就可以很容易地看到在出現bug前,調用了什麼函數。
在產生調試輸出時,.net Framework有一些新的功能可以用。System.Diagnostic.Debug類可以讓你格式化調試輸出,並能很容易地建立不同的類或層級的調試輸出。下面是我喜歡用的一些指導方針。
首先,在你的程式中為每個類建一個traceswitch對象:
以下是引用片段: public class MyClass { private static TraceSwitch myClassSwitch = new TraceSwitch ("MyClassSwitch", "Controls the / debug output of MyClass");} |
然後,用WriteIf() 和 WriteLineIf() 方法來記錄任何你覺得有助於你跟蹤你的程式的資訊:
以下是引用片段: public bool ProcessIterations (int numIters) { WriteLineIf (myClassSwitch.TraceInfo, "Entering ProcessIterations", "CallTrace"); ImOK (); Debug.Assert (numIters > 0, "ProcessIterations.", "Iterations must be more than 0");} |
我更喜歡用WriteLineIf(),它可以列印出錯誤訊息以及錯誤種類。第一個參數包含一個用於調試開關的值,可以讓你控制列印什麼層級的輸出。
System.Diagnostics.Trace 的運用同Debug的用法完全一樣。 不同的地方是,Debug只編譯到Debug版本中,而Trace語句編譯到Debug和Release版本中。因此,運用Trace語句應更謹慎。將Trace語句用到可以在編程實戰中幫你發現bugs或捕獲使用特徵的代碼中。
為什麼用這種方法? 運用這些方法可以讓你知道代碼執行的順序。這有助於你確定在程式出錯前有何動作(actions)。
動態地控制輸出
這些新的.NET Framework類的最大的好處是通過編輯一個設定檔,你可以改變任何跟蹤開關(trace switch) 的層級。在應用程式目錄中建一個XML檔案,該檔案的名字與你的程式的名字相同,副檔名為“.config”。例如,如果你的程式是myApp.exe,就建一個myApp.exe.config。你可以用這個檔案來設定你的跟蹤開關的值。比如下面的檔案:
以下是引用片段: <?xml version="1.0"?> <configuration> <system.diagnostics> <switches> <add name="MyClassSwitch" value="4" /> </switches> </system.diagnostics> </configuration> |
該檔案把MyClassSwitch的值設定為4,符合“Info”的設定。僅通過編輯這個config檔案,你就可以改變你的程式中任何開關的層級。
為什麼用這種方法?通過運用多個開關和建立適當的config檔案,你可以改變記錄輸出,集中精力到你關心的那些元素上。
設定你的listeners
.NET Framework有一個代表接收Debug、Assert和Trace輸出的對象的Listeners集合。預設情況下,你的應用程式有一個單一的DefaultTraceListener。這個listener忽略Debug和Trace輸出,顯示一個Assert訊息的對話方塊。你可以添加項目到這個集合,或從這個集合中刪除項目。已為你建立好的兩項是TextWriterTraceListener和EventLogTraceListener。TextWriterTraceListener把訊息寫到一個流(stream)中,EventLogTraceListener把訊息寫到一個EventLog中。EventLog可以讓你把你的程式的調試和跟蹤訊息寫到系統事件日誌記錄中。
我喜歡為所有程式建一個調試記錄檔:
以下是引用片段: static void Main () { Debug.Listeners.Add ( New TextWriteTraceListener ("MyLog.log"); // etc. |
為什麼用這個方法? 該技巧可以讓你控制在哪裡用調試和跟蹤語句。
在你發現bugs時,用這些技巧.
坦白地說,在開始寫代碼時,沒有人用所有這些技巧。實際上,當我們努力想發現那些重大bugs的原因時我們通常添加這些語句。在你陷入那種困境時,嘗試以下方法:
1. 當你建立類時,通常為每個類建一個跟蹤開關。
2. 通常為每個類建一個驗證函式。
3. 當你要診斷錯誤的行為時,添加其它的跟蹤和調試語句。確信把這些變化保留在代碼中。我發現的最常見的錯誤之一就是:當程式員想找到bugs時,他們添加許多跟蹤和調試語句來發現錯誤。然後,一旦他們找到了他們尋找的錯誤,他們就把這些語句刪掉。
這些工具會幫你發現並修理bugs,就看你用不用它們了。