明輝手游網(wǎng)中心:是一個免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺!

6種迅速修理C# Bug的方法

[摘要].NET Framework中包含許多工具可以用來更快、更容易地編寫正確的程序。但我們得面臨這樣的情況:出現(xiàn)bugs。不管程序多么簡單,程序員都可能出錯。根據(jù)我的經(jīng)驗,大多數(shù)程序的bugs出現(xiàn)在程序...
.NET Framework中包含許多工具可以用來更快、更容易地編寫正確的程序。但我們得面臨這樣的情況:出現(xiàn)bugs。不管程序多么簡單,程序員都可能出錯。根據(jù)我的經(jīng)驗,大多數(shù)程序的bugs出現(xiàn)在程序員之間的接口:當(dāng)一個程序員編寫的代碼被另一個程序員調(diào)用時。不知何故,調(diào)用者破壞了代碼編寫時做的假設(shè)。是誰的過錯呢?這并不要緊,更重要的是你能多快修好它?下面這些技巧將幫你在程序投入使用前更快地發(fā)現(xiàn)并解決這些問題。最終,這些技巧會幫你診斷任何的確在使用中出現(xiàn)的問題。

測試假設(shè)條件
測試假設(shè)條件是構(gòu)建正確的程序最重要的一個方法。在你寫一個函數(shù)時,你應(yīng)該考慮并確定你對那個函數(shù)做了什么樣的假設(shè)。你應(yīng)該問自己以下這些問題:

1. 當(dāng)這個函數(shù)被調(diào)用時,這個對象必須是怎樣的(對象初試化,某個內(nèi)在變量值)?  
2. 當(dāng)這個函數(shù)存在時,這個對象將會怎樣(仍是#1,但包括該函數(shù)的副作用)?
3. 該函數(shù)的任何參數(shù)必須是怎樣的(允許空值嗎,輸入值的范圍是什么)?
4. 返回值必須是怎樣的?  


一旦你問了自己這四個問題并作出回答后,把答案放到代碼中。在C#中,用System.Diagnostics.Debug類的Assent方法來表示: public bool ProcessIterations (int
numIters)
{
Debug.Assert (numIters > 0,
"ProcessIterations.",
"Iterations must be more than 0");
// More code...



該代碼片段執(zhí)行了numIters參數(shù)必須大于零這樣一個假設(shè)。如果你用一個無效的參數(shù)調(diào)用ProcessIterations,該Assert被觸發(fā)。這時候,程序停止運(yùn)行并通知用戶出現(xiàn)的錯誤。聲明(Assertions)只被編譯到Debug 版本中的程序,所以它們不影響生產(chǎn)情況中的性能。

為什么用這種方法? 運(yùn)用這種技巧可以確保很快地發(fā)現(xiàn)對你的類的方法未預(yù)料地使用。然后,或者調(diào)用者修改他的代碼,或者要求在你的類的行為(behavior)中修改。




驗證完整性
一個C# 程序中的大多數(shù)函數(shù)都是一個對象上的實例方法。對任何對象的有效狀態(tài)都有暗示的假設(shè)。當(dāng)一個公有的方法被調(diào)用時,你應(yīng)該確保那些暗示的假設(shè)經(jīng)過了測試。C# 的條件編譯特征使這一點(diǎn)很容易實現(xiàn)。

首先,寫一個私用的函數(shù)來測試對象的完整性。你在這么做時,將該方法標(biāo)為“Conditional”: [Conditional ("DEBUG")]
private void ImOK ()
{
Debug.Assert (this != null,
"Testing Object State",
"this cannot be null");
// More here.
}




然后,在每個公有的方法中,調(diào)用ImOK方法: public bool ProcessIterations (int
numIters)
{
ImOK ();
Debug.Assert (numIters > 0,
"ProcessIterations.",
"Iterations must be more than 0");




在發(fā)布(Release)版本中,編譯器自動取消對ImOK 的調(diào)用。

為什么用這種方法?運(yùn)用該技巧,你可以快速發(fā)現(xiàn)任何你的對象狀態(tài)變得無效的情況。

運(yùn)用Debug 和Trace 輸出
打印診斷消息可以幫你確定你的程序是怎么出錯的。你需要知道當(dāng)觸發(fā)一個Assert時,發(fā)生了什么情況;你也通常需要知道在這之前發(fā)生了什么。知道這些的最好的方式就是運(yùn)用你的代碼,這樣你就可以很容易地看到在出現(xiàn)bug前,調(diào)用了什么函數(shù)。

在生成調(diào)試輸出時,.NET Framework有一些新的功能可以用。System.Diagnostic.Debug類可以讓你格式化調(diào)試輸出,并能很容易地創(chuàng)建不同的類或級別的調(diào)試輸出。下面是我喜歡用的一些指導(dǎo)方針。

首先,在你的程序中為每個類建一個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(),它可以打印出錯誤消息以及錯誤種類。第一個參數(shù)包含一個用于調(diào)試開關(guān)的值,可以讓你控制打印什么級別的輸出。




System.Diagnostics.Trace 的運(yùn)用同Debug的用法完全一樣。 不同的地方是,Debug只編譯到Debug版本中,而Trace語句編譯到Debug和Release版本中。因此,運(yùn)用Trace語句應(yīng)更謹(jǐn)慎。將Trace語句用到可以在編程實戰(zhàn)中幫你發(fā)現(xiàn)bugs或捕獲使用特征的代碼中。

為什么用這種方法? 運(yùn)用這些方法可以讓你知道代碼執(zhí)行的順序。這有助于你確定在程序出錯前有何動作(actions)。

動態(tài)地控制輸出
這些新的.NET Framework類的最大的好處是通過編輯一個配置文件,你可以改變?nèi)魏胃欓_關(guān)(trace switch) 的級別。在應(yīng)用程序目錄中建一個XML文件,該文件的名字與你的程序的名字相同,擴(kuò)展名為“.config”。例如,如果你的程序是myApp.exe,就建一個myApp.exe.config。你可以用這個文件來設(shè)置你的跟蹤開關(guān)的值。比如下面的文件: <?xml version="1.0"?>
<configuration>
<system.diagnostics>
<switches>
<add name="MyClassSwitch"
value="4" />
</switches>
</system.diagnostics>
</configuration>




該文件把MyClassSwitch的值設(shè)置為4,符合“Info”的設(shè)置。僅通過編輯這個config文件,你就可以改變你的程序中任何開關(guān)的級別。

為什么用這種方法?通過運(yùn)用多個開關(guān)和創(chuàng)建適當(dāng)?shù)腸onfig文件,你可以改變記錄輸出,集中精力到你關(guān)心的那些元素上。

設(shè)置你的Listeners
.NET Framework有一個代表接收Debug、Assert和Trace輸出的對象的Listeners集合。默認(rèn)情況下,你的應(yīng)用程序有一個單一的DefaultTraceListener。這個listener忽略Debug和Trace輸出,顯示一個Assert消息的對話框。你可以添加項目到這個集合,或從這個集合中刪除項目。已為你創(chuàng)建好的兩項是TextWriterTraceListener和EventLogTraceListener。TextWriterTraceListener把消息寫到一個流(stream)中,EventLogTraceListener把消息寫到一個EventLog中。EventLog可以讓你把你的程序的調(diào)試和跟蹤消息寫到系統(tǒng)事件日志記錄中。

我喜歡為所有程序建一個調(diào)試日志文件: static void Main ()
{
Debug.Listeners.Add (
New TextWriteTraceListener
("MyLog.log");
// etc.




為什么用這個方法? 該技巧可以讓你控制在哪里用調(diào)試和跟蹤語句。

在你發(fā)現(xiàn)Bugs時,用這些技巧
坦白地說,在開始寫代碼時,沒有人用所有這些技巧。實際上,當(dāng)我們努力想發(fā)現(xiàn)那些重大bugs的原因時我們通常添加這些語句。在你陷入那種困境時,嘗試以下方法:

1. 當(dāng)你創(chuàng)建類時,通常為每個類建一個跟蹤開關(guān)。  
2. 通常為每個類建一個驗證函數(shù)。  
3. 當(dāng)你要診斷錯誤的行為時,添加其它的跟蹤和調(diào)試語句。確信把這些變化保留在代碼中。我發(fā)現(xiàn)的最常見的錯誤之一就是:當(dāng)程序員想找到bugs時,他們添加許多跟蹤和調(diào)試語句來發(fā)現(xiàn)錯誤。然后,一旦他們找到了他們查找的錯誤,他們就把這些語句刪掉。


這些工具會幫你發(fā)現(xiàn)并修理bugs,就看你用不用它們了。


關(guān)于作者:
Bill Wagner 是SRT Solutions的創(chuàng)始人和顧問,專門研究.NET 開發(fā)。他是The C# Core Language Little Black Book一書的作者。 十多年來,他一直從事軟件開發(fā)與教學(xué)工作。他是Visual Studio Magazine的專欄作家,并在許多關(guān)于改進(jìn)軟件開發(fā)習(xí)慣的會議上做演講。他從事的軟件程序的范圍很廣,從兒童游戲到生物信息學(xué)研究。聯(lián)系方式:wwagner@SRTsolutions.com。